【BZOJ】4259: 残缺的字符串 FFT
【题意】给定长度为m的匹配串B和长度为n的模板串A,求B在A中出现多少次。字符串仅由小写字母和通配符" * "组成,其中通配符可以充当任意一个字符。n<=3*10^5。
【算法】FFT
【题解】假设模板串的数组A用0~26代表所有字符,0为通配符,匹配串的数组B同理,那么用表示差异的经典套路:
$$C_n=\sum_{i=0}^{m-1}(A_{n+i}-B_i)^2*A_{n+i}*B_i$$
那么可以看出$C_n=0$当且仅当$S_A[n,n+m-1]=S_B[0,m-1]$。这里的通配符为0,所以当含有通配符时式子直接为0,即无差异。
将数组A反转,得到:
$$C_x=\sum_{i=0}^{m-1}(A'_{n-x-i-1}-B_i)^2*A'_{n-x-i-1}*B_i$$
尝试扩展上届:
$$C_x=D_{n-x-1}=\sum_{i=0}^{n-x-1}(A'_{n-x-i-1}-B_i)^2*A'_{n-x-i-1}*B_i$$
现在只需要计算Dx了,二项式拆分得到:
$$D_x=\sum_{i=0}^{x}A_{x-i}^3B_i-2A_{x-i}^2B_i^2+A_{x-i}B_i^3$$
(这里公式不知道A后为什么不能加单引号,不然latex会出错)
复杂度O(n log n)。
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; const int maxn=1100010,N=300010; const double PI=acos(-1); int m,n,aa[N],bb[N],d[N]; char A[N],B[N]; struct cp{ double x,y; cp(double a,double b){x=a;y=b;} cp(){x=y=0;} cp operator + (cp a){return cp(x+a.x,y+a.y);} cp operator - (cp a){return cp(x-a.x,y-a.y);} cp operator * (cp a){return cp(x*a.x-y*a.y,x*a.y+y*a.x);} }A1[maxn],A2[maxn],A3[maxn],B1[maxn],B2[maxn],B3[maxn]; void fft(cp *a,int n,int f){ int k=0; for(int i=0;i<n;i++){ if(i<k)swap(a[i],a[k]); for(int j=n>>1;(k^=j)<j;j>>=1); } for(int l=2;l<=n;l<<=1){ int m=l/2; cp wn(cos(2*PI*f/l),sin(2*PI*f/l)); for(cp *p=a;p!=a+n;p+=l){ cp w(1,0); for(int i=0;i<l/2;i++){ cp t=w*p[i+m]; p[i+m]=p[i]-t; p[i]=p[i]+t; w=w*wn; } } } if(f==-1)for(int i=0;i<n;i++)a[i].x/=n; } int main(){ scanf("%d%d%s%s",&m,&n,B,A); for(int i=0;i<n;i++)aa[n-i-1]=(A[i]=='*'?0:A[i]-'a'+1); for(int i=0;i<m;i++)bb[i]=(B[i]=='*'?0:B[i]-'a'+1); for(int i=0;i<n;i++)A1[i]=cp(aa[i],0),A2[i]=cp(aa[i]*aa[i],0),A3[i]=cp(aa[i]*aa[i]*aa[i],0); for(int i=0;i<m;i++)B1[i]=cp(bb[i],0),B2[i]=cp(bb[i]*bb[i],0),B3[i]=cp(bb[i]*bb[i]*bb[i],0); int N=1;while(N<n+m)N<<=1; fft(A1,N,1);fft(B1,N,1); fft(A2,N,1);fft(B2,N,1); fft(A3,N,1);fft(B3,N,1); for(int i=0;i<N;i++)A1[i]=A3[i]*B1[i]-cp(2,0)*A2[i]*B2[i]+A1[i]*B3[i]; fft(A1,N,-1); int cnt=0; for(int i=0;i<=n-m;i++)if(!((int)(A1[n-1-i].x+0.1)))d[++cnt]=i+1; printf("%d\n",cnt); for(int i=1;i<=cnt;i++)printf("%d ",d[i]); return 0; }