[BZOJ4503]两个串(快速傅立叶变换FFT)

来源:互联网 发布:反美颜软件有可能 编辑:程序博客网 时间:2024/06/01 07:49

Description
兔子们在玩两个串的游戏。给定两个字符串S和T,兔子们想知道T在S中出现了几次,
分别在哪些位置出现。注意T中可能有“?”字符,这个字符可以匹配任何字符。

Input
两行两个字符串,分别代表S和T

Output
第一行一个正整数k,表示T在S中出现了几次
接下来k行正整数,分别代表T每次在S中出现的开始位置。按照从小到大的顺序输出,S下标从0开始。

Sample Input
bbabaababaaaaabaaaaaaaabaaabbbabaaabbabaabbbbabbbbbbabbaabbbababababbbbbbaaabaaabbbbbaabbbaabbbbabab
a?aba?abba

Sample Output
0

HINT
S 长度不超过 10^5, T 长度不会超过 S。 S 中只包含小写字母, T中只包含小写字母和“?”


这题酷似kmp,但是由于有通配符”?”的存在,所以并不能用。
那么可以把“匹配”这个概念转化为
(c1c2)2=0
因为有通配符的存在
c2(c1c2)2=0
那么子串匹配也就是求

lenT1j=0Tj(SiTj)2=0

把这个式子展开来

lenT1j=0Tj(S2i2SiTj+T2j)=0

lenT1j=0TjS2i2SiT2j+T3j=0

其实i可以表示成j+x那么这条式子就可以表示成

lenT1j=0TjS2j+x2Sj+xT2j+T3j=0

进而

lenT1j=0TjS2j+xlenT1j=02Sj+xT2j+lenT1j=0T3j=0

最后一个我们暂时不用去管,先看前面两个。
因为卷积满足函数的下标和不变,但是这两个是差不变,那么我们考虑把其中一个函数反转,使其满足卷积下标和不变的性质。
Ti=TlenTi1

lenT1j=0TlenTj1S2j+xlenT1j=02Sj+xT2lenTj1+lenT1j=0T3lenTj1=0

然后我们再发现
lenTj1+j+x=lenT+x1lenT1
但事实上,这是等效的,因为lenT1 ~ lenT+x1的这一段我们都可以把它看作0。
那么三次fft求解即可


code:

#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#include<iostream>using namespace std;typedef long long LL;const int maxn=110000;const double pi=acos(-1.0);struct complex{    double r,i;    complex() {}    complex(double _r,double _i) {r=_r,i=_i;}    friend complex operator + (const complex &x,const complex &y) {return complex(x.r+y.r,x.i+y.i);}    friend complex operator - (const complex &x,const complex &y) {return complex(x.r-y.r,x.i-y.i); }    friend complex operator * (const complex &x,const complex &y) {return complex(x.r*y.r-x.i*y.i,x.r*y.i+x.i*y.r);}} a[maxn*4],b[maxn*4],c[maxn*4];int R[maxn*4];void fft(complex *y,int len,int on){    for(int i=0; i<len; i++)if(i<R[i])swap(y[i],y[R[i]]);    for(int i=1; i<len; i<<=1)    {        complex wn(cos(pi/i),sin(on*pi/i));        for(int j=0; j<len; j+=(i<<1))        {            complex w(1,0);            for(int k=0; k<i; k++,w=w*wn)            {                complex u=y[j+k];                complex v=w*y[j+k+i];                y[j+k]=u+v;                y[j+k+i]=u-v;            }        }    }    if(on==-1)for(int i=0; i<len; i++)y[i].r/=len;}inline int get(char c){    if(c>='a' && c<='z') return c-'a'+1;    else return 0;}char s1[maxn*4],s2[maxn*4];int S[maxn*4],T[maxn*4];int main(){    int lenS,lenT;    scanf("%s",s1);    lenS=strlen(s1);    for(int i=0; i<lenS; i++) S[i]=get(s1[i]);    scanf("%s",s2);    lenT=strlen(s2);    for(int i=0; i<lenT; i++) T[i]=get(s2[lenT-i-1]);    int m=lenS+lenT,n,L=0;    for(n=1; n<=m; n*=2) L++;    for(int i=0; i<n; i++) R[i]=(R[i>>1]>>1)|(i&1)<<(L-1);    for(int i=0; i<n; i++) a[i]=complex(S[i]*S[i],0);    for(int i=0; i<n; i++) b[i]=complex(T[i],0);    fft(a,n,1);    fft(b,n,1);    for(int i=0; i<n; i++) c[i]=c[i]+a[i]*b[i];    for(int i=0; i<n; i++) a[i]=complex(1,0);    for(int i=0; i<n; i++) b[i]=complex(T[i]*T[i]*T[i],0);    fft(a,n,1);    fft(b,n,1);    for(int i=0; i<n; i++) c[i]=c[i]+a[i]*b[i];    for(int i=0; i<n; i++) a[i]=complex(S[i]*2,0);    for(int i=0; i<n; i++) b[i]=complex(T[i]*T[i],0);    fft(a,n,1);    fft(b,n,1);    for(int i=0; i<n; i++) c[i]=c[i]-a[i]*b[i];    fft(c,n,-1);    int ans=0;    for(int i=0; i<lenS-lenT+1; i++) if(c[i+lenT-1].r<0.5) ans++;    printf("%d\n",ans);    for(int i=0; i<lenS-lenT+1; i++) if(c[i+lenT-1].r<0.5) printf("%d\n",i);    return 0;}
原创粉丝点击