BZOJ 4503 两个串 FFT

Solution

题目要求:
给定两个串,问第二个串在第一个串的哪些位置出现过
第二个串中有通配符’?’

乍一眼看上去是一个KMP?
但是因为通配符的存在使得nxt 数组直接报废

当然,DP 也是可以的
但是自己感受那令人绝望的时间复杂度吧

考虑将其转化为数字串做减法
设第一个串为a ,第二个串为b ,长度分别为la,lb
在没有通配符的情况下
若两串在某个位置匹配,则一定有

i=xx+lb1(a[i]b[i])2=0

拆开
i=xx+lb1a[i]2+b[i]22a[i]b[i]=0

发现很好化为卷积和的形式,只需要将b 反序即可
i=xlb+1xa[i]2+b[xi]22a[i]b[xi]=0

PS:这里的x 换了一下,这是为了卷积方便

所以将a ,b 卷积一下即可

现在考虑加入通配符?
我们要求不管是a[i]=b[i] 还是b[i]=? 其结果都是0
那么将整体都乘上一个b[i] 即可

原式变为

i=xlb+1xa[i]2b[xi]+b[xi]32a[i]b[xi]2=0

并且分析发现,b[xi]3 是不需要卷积的
因为若匹配,则b 的每一位都需要贡献一个b[i]3 ,所以直接预处理出来

代码如下:

#include <bits/stdc++.h>
using namespace std;
const int N =100010;
const double pi=acos(-1);
char str[N];
struct Complex {
    double x;
    double y;
    Complex() {};
    Complex(double _x,double _y) { x=_x;y=_y; }
    Complex operator + (const Complex o) { return Complex(x+o.x,y+o.y); }
    Complex operator - (const Complex o) { return Complex(x-o.x,y-o.y); }
    Complex operator * (const Complex o) { return Complex(x*o.x-y*o.y,x*o.y+y*o.x); }
}a[N<<2],_a[N<<2],b[N<<2],_b[N<<2];
int r[N<<2];
int n,m,L,R,sum;
void fft(Complex *now,int f,int n) {
    for(int i=0;i<n;++i)
        if(i<r[i]) swap(now[i],now[r[i]]);
    for(int i=1;i<n;i<<=1) {
        Complex wn(cos(pi/i),f*sin(pi/i));//w_n^i;
        for(int j=0;j<n;j+=(i<<1)) {
            Complex w(1,0);
            for(int k=0;k<i;++k,w=w*wn) {
                Complex x=now[j+k],y=w*now[j+k+i];
                now[j+k]=x+y;
                now[j+k+i]=x-y;
            }
        }
    }
    if(f==-1)
        for(int i=0;i<n;++i)
            now[i].x/=n;
}
int main() {
    scanf("%s",str);
    R=n=strlen(str);
    for(int i=0;i<n;++i) {
        a[i].x=str[i]-'a'+1;
        _a[i].x=a[i].x*a[i].x;//a^2
    }
    scanf("%s",str);
    L=m=strlen(str);
    for(int i=0;i<m;++i) {
        if(str[i]!='?') b[m-i-1].x=str[i]-'a'+1;
        sum+=b[m-i-1].x*b[m-i-1].x*b[m-i-1].x;//b^3
        _b[m-i-1].x=b[m-i-1].x*b[m-i-1].x;//b^2
    }
    m+=n;
    int nn=0;
    for(n=1;n<=m;n<<=1) nn++;
    for(int i=0;i<n;++i) { r[i]=(r[i>>1]>>1)|((i&1)<<(nn-1)); }

    fft(_a,1,n);fft(b,1,n);
    for(int i=0;i<=n;++i) { b[i]=_a[i]*b[i]; }
    fft(b,-1,n);
    //*******a^2*b
    fft(a,1,n);fft(_b,1,n);
    for(int i=0;i<=n;++i) { a[i]=a[i]*_b[i]; }
    fft(a,-1,n);
    //*******a*b^2
    for(int i=0;i<=n;++i) {
        a[i].x=(int)(a[i].x+0.5);
        b[i].x=(int)(b[i].x+0.5);
    }
    int times=0;
    for(int i=L-1;i<=R-1;++i)
        if(b[i].x+sum-2*a[i].x==0)
            ++times;
    printf("%d\n",times);
    for(int i=L-1;i<=R-1;++i)
        if(b[i].x+sum-2*a[i].x==0)
            printf("%d\n",i-L+1);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章