bzoj4503 两个串

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4503

【题解】

我们设匹配函数f = (a[i]-b[i])^2*b[i]

那么展开f,做卷积就能得出f的值了

对于t[i]==‘?‘,b[i]=0,显然当f=0表示匹配,那么直接FFT即可。

我记得有道类似的题,两串都有通配符匹配,就把f改成(a[i]-b[i])^2a[i]b[i]就ok了。

# include <math.h>
# include <stdio.h>
# include <string.h>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 5e5 + 10;
const int mod = 1e9+7;
const double pi = acos(-1.0); 

# define RG register
# define ST static

struct cp {
    double x, y;
    cp() {}
    cp(double x, double y) : x(x), y(y) {}
    friend cp operator +(cp a, cp b) {
        return cp(a.x+b.x, a.y+b.y);
    }
    friend cp operator -(cp a, cp b) {
        return cp(a.x-b.x, a.y-b.y);
    }
    friend cp operator *(cp a, cp b) {
        return cp(a.x*b.x-a.y*b.y, a.x*b.y+a.y*b.x);
    }
};

char str[M];
int S[M], T[M];
int n1, n2;

namespace FFT {
    const int M = 6e5 + 10;
    cp w[2][M];
    int n, lst[M];
    inline void init(int _n) {
        n = 1;
        while(n < _n) n <<= 1;
        for (int i=0; i<n; ++i) w[0][i] = cp(cos(pi*2/n*i), sin(pi*2/n*i)), w[1][i] = cp(w[0][i].x, -w[0][i].y);
        int len = 1;
        while((1<<len) < n) len ++;
        for (int i=0; i<n; ++i) {
            int t = 0;
            for (int j=0; j<len; ++j) if(i&(1<<j)) t |= (1<<(len-j-1));
            lst[i] = t;
        }
    }
    inline void DFT(cp *a, int op) {
        cp *o = w[op];
        for (int i=0; i<n; ++i) if(i < lst[i]) swap(a[i], a[lst[i]]);
        for (int len=2; len<=n; len<<=1) {
            int m = len>>1;
            for (cp *p = a; p != a+n; p+=len) {
                 for (int k=0; k<m; ++k) {
                      cp t = o[n/len*k] * p[k+m];
                     p[k+m] = p[k] - t;
                     p[k] = p[k] + t; 

                }
            }
        }
        if(op == 1) {
            for (int i=0; i<n; ++i) a[i].x = a[i].x/(double)n, a[i].y = a[i].y/(double)n;
        }
    }
}

cp a[M], b[M], c[M];
double cnt = 0;
int ans[M], ansn = 0; 

int main() {
    scanf("%s", str);
    n1 = strlen(str);
    for (int i=0; i<n1; ++i) S[i] = str[i] - ‘a‘ + 1;
    scanf("%s", str);
    n2 = strlen(str);
    for (int i=0; i<n2; ++i) {
        if(str[i] == ‘?‘) T[i] = 0;
        else T[i] = str[i] - ‘a‘ + 1;
    }
    int m = n1 + n2 - 1;
    FFT::init(m);
    for (int i=0; i<FFT::n; ++i) a[i].x = a[i].y = b[i].x = b[i].y = 0;
    for (int i=0; i<n1; ++i) a[i].x = S[i]*S[i];
    for (int i=0; i<n2; ++i) {
        int pos = n2 - 1 - i;
        b[pos].x = T[i];
    }
    FFT::DFT(a, 0); FFT::DFT(b, 0);
    cnt = 0;
    for (int i=0; i<FFT::n; ++i) c[i] = a[i] * b[i];
    for (int i=0; i<FFT::n; ++i) a[i].x = a[i].y = b[i].x = b[i].y = 0;
    for (int i=0; i<n1; ++i) a[i].x = S[i]*2;
    for (int i=0; i<n2; ++i) {
        int pos = n2 - 1 - i;
        b[pos].x = T[i]*T[i];
        cnt = cnt + T[i]*T[i]*T[i];
    }
    FFT::DFT(a, 0); FFT::DFT(b, 0);
    for (int i=0; i<FFT::n; ++i) c[i] = c[i] - a[i] * b[i];
    FFT::DFT(c, 1);
    for (int i=0; i<=n1-n2; ++i)
        if(int(c[n2-1+i].x + cnt + 0.5) == 0) ans[++ansn] = i;
    printf("%d\n", ansn);
    for (int i=1; i<=ansn; ++i) printf("%d\n", ans[i]);
    return 0;
}

时间: 2024-11-05 16:04:31

bzoj4503 两个串的相关文章

bzoj4503: 两个串 bitset

题目链接 bzoj4503: 两个串 题解 暴一发bitset f[i][j] 表示 S[1..i] 是否有个后缀能匹配 T[1..j] 那么假设 S[i+1] 能匹配 T[s],令 f[i+1][s] | = f[i][s-1] 所以预处理理出每个字符能匹配 T的哪些位置,设为[c] 那么 f[i]=((f[i-1]<<1)|(1<<1)) & mat[S[i]] 直接在mat上做匹配就好了 时间复杂度:O(|S||T|/32) 代码 #include<cstdio

BZOJ4503 两个串 FFT

题目传送门 - BZOJ4503 题意概括 给定两个字符串S和T,回答T在S中出现了几次,在哪些位置出现.注意T中可能有?字符,可以匹配任何字符. 题解 首先,假装你已经知道了这是一道$FFT$题. 考虑怎样$FFT$. 字符串匹配的时候,对于匹配成功的对应字母的编号(比如分别是$i$和$j$),满足了$i-j$都相同.但是我们需要的是$i+j$都相等. 于是我们用$FFT$的经典套路,翻转$T$串. 我们构造一个卷积: $\sum_{i=0}^{n}\sum_{j=0}^{m}(S_{i}-T

bzoj4403 两个串

Description 兔子们在玩两个串的游戏.给定两个字符串S和T,兔子们想知道T在S中出现了几次, 分别在哪些位置出现.注意T中可能有“?”字符,这个字符可以匹配任何字符. Input 两行两个字符串,分别代表S和T Output 第一行一个正整数k,表示T在S中出现了几次 接下来k行正整数,分别代表T每次在S中出现的开始位置.按照从小到大的顺序输出,S下标从0开始. 两个串a,b相等(b中有通配符)当且仅当Σ(a[i]-b[i])2b[i]=0,其中a[i],b[i]为对应字符的对应编号,

UESTC 883 方老师与两个串

CF原题 由题可知,n,m太大,无法开出dp[n][m]的数组. 观察发现s/e最大为300,也就是说,选用第一种操作的次数不会超过300. 于是定义dp[i][j],第一个串的前i个数,使用了j次第一种操作的时候,第二个串最少删了多少个数. 转移有两种情况: 1.当前位置不删,这时dp[i][j]=dp[i-1][j]: 2.当前位置删,此时就需要在B串中找和当前位置的数相同的数的位置,并且只有在找到的位置大于dp[i-1][j-1]的时候才是可行的.为了保证dp[i][j]最小,显然就是找大

数据结构——算法之(032)(求两个串中的第一个最长子串)

[申明:本文仅限于自我归纳总结和相互交流,有纰漏还望各位指出. 联系邮箱:[email protected]] 题目: 求两个串中的第一个最长子串(神州数码曾经试题).如"abractyeyt","dgdsaeactyey"的最大子串为"actyey". 题目分析: 1.这里仅仅是实现了简单的字符串算法(最大支持字符串长度64),主要是展示算法思想 2.思路是把2个字符串每一个字符的匹配关系,映射到一张二维数组表中,匹配写1,非匹配写0 算法实现

求两个串的最大子序列(非字串)

问题:求两个串的最大子序列(并非连接的) Java代码: import java.util.Set; import java.util.StringJoiner; public class Main { public static int getL(String a, String b) { if (a.isEmpty()||b.isEmpty()) return 0; if (a.charAt(0) == b.charAt(0)) return getL(a.substring(1), b.s

SPOJ 1811 Longest Common Substring(求两个串的最长公共子串)

http://www.spoj.com/problems/LCS/ 题目:求两个串的最长公共子串 分析: 以A建立SAM 让B在SAM上匹配可以类比于kmp思想,我们知道在Parent树上,fa是当前节点的子集,也就是说满足最大前缀,利用这个就可以做题了 #include <bits/stdc++.h> #define LL long long #define P pair<int, int> #define lowbit(x) (x & -x) #define mem(a

BZOJ 4503 两个串(FFT)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=4503 [题目大意] 给出S串和T串,计算T在S中出现次数,T中有通配符'?'. [题解] 我们定义f[x]=sum_{i=0}^{n-1}|s1[i]-s2[i]|,当f[x]=0时,两个字符串相等.因为考虑到这里还有适配符,所以用f[x]=sum_{i=0}^{n-1}(s1[i]-s2[i])*(s1[i]-s2[i])*s1[i]*s2[i]来表示匹配函数.我们可以发现,如果将

BZOJ 4503 两个串 ——FFT

[题目分析] 定义两个字符之间的距离为 (ai-bi)^2*ai*bi 如果能够匹配,从i到i+m的位置的和一定为0 但这和暴力没有什么区别. 发现把b字符串反过来就可以卷积用FFT了. 听说KMP+暴力可以卡到100ms以内(雾) [代码] #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using na