题链:
http://codevs.cn/problem/4158/
题解:
FFT。
定义两个相同长度的字符串s1,s2的距离为
$$dis(s1,s2)=sum_{i=0}^{len-1}(s1[i]-s2[i])^2s1[i]s2[i]$$
如果两个字符串相同,那么dis=0。
(对于本题而言,只需把通配符用0表示,其它字符c用c-'a'+1表示。)
然后看看如何求出文本串T(长度为n)和模式串S(长度为m)的总共匹配数。
定义T串从$l$位置开始的长度为m的子串和S的距离为$D(l)$,那么:
$$D(l)=sum_{k=0}^{m-1}(T[l+k]-S[k])^2T[l+k]S[k]$$
为了构成卷积形式,我们把S串翻转,即
$$D(l)=D'(l+m-1)=sum_{k=0}^{m-1}(T[l+k]-S[m-1-k])^2T[l+k]S[m-1-k]$$
然而这个仍然不是卷积形式,我们再继续化一下式子:(令D(l)=D'(l+m-1))
$$egin{aligned}
D'(l+m-1)&=sum_{k=0}^{m-1}(T[l+k]-S[m-1-k])^2T[l+k]S[m-1-k]\
&=sum_{k=0}^{m-1}(T[l+k]^3S[m-1-k]-2T[l+k]^2S[m-1-k]^2+T[l+k]S[m-1-k]^3)\
&=sum_{k=0}^{m-1}T[l+k]^3S[m-1-k]+sum_{k=0}^{m-1}-2T[l+k]^2S[m-1-k]^2+sum_{k=0}^{m-1}T[l+k]S[m-1-k]^3
end{aligned}$$
然后逮着这个式子做3组卷积即可。
代码:
#include<bits/stdc++.h> #define MAXN 1048577 #define INF 0x3f3f3f3f using namespace std; const double Pi=acos(-1),eps=1e-6; typedef complex<double>Complex; Complex null(0,0); int A[MAXN],B[MAXN],order[MAXN]; int idx(char ch){ if(ch=='*') return 0; return ch-'a'+1; } void getstring(int *s,int len){ static char ch; for(int i=0;i<len;i++) scanf(" %c",&ch),s[i]=idx(ch); } void FFT(Complex *Y,int n,int sign){ for(int i=1;i<n;i++) if(i<order[i]) swap(Y[i],Y[order[i]]); for(int d=2;d<=n;d<<=1){ Complex dw(cos(2*Pi/d),sin(sign*2*Pi/d)),w,tmp; for(int i=0;w=Complex(1,0),i<n;i+=d) for(int k=i;k<i+d/2;w=w*dw,k++) tmp=w*Y[k+d/2],Y[k+d/2]=Y[k]-tmp,Y[k]=Y[k]+tmp; } } int main(){ static int pos[MAXN]; static Complex f1[MAXN],g1[MAXN],f2[MAXN],g2[MAXN],f3[MAXN],g3[MAXN],D[MAXN]; int n,m,N,len,ans=0; scanf("%d%d",&n,&m); getstring(A,n); getstring(B,m); reverse(A,A+n); for(N=1,len=0;N<n+m-1;N<<=1) len++; for(int i=1;i<N;i++) order[i]=(order[i>>1]>>1)|((i&1)<<(len-1)); for(int i=0;i<n;i++){ f1[i]=Complex(A[i]*A[i]*A[i],0); f2[i]=Complex(A[i]*A[i],0); f3[i]=Complex(A[i],0); } for(int i=0;i<m;i++){ g1[i]=Complex(B[i],0); g2[i]=Complex(B[i]*B[i],0); g3[i]=Complex(B[i]*B[i]*B[i],0); } FFT(f1,N,1); FFT(g1,N,1); FFT(f2,N,1); FFT(g2,N,1); FFT(f3,N,1); FFT(g3,N,1); for(int i=0;i<N;i++) D[i]=f1[i]*g1[i]-2.0*f2[i]*g2[i]+f3[i]*g3[i]; FFT(D,N,-1); for(int i=0;i<m-n+1;i++) if((int)((D[i+n-1].real()+0.5)/N)==0) pos[++ans]=i+1; printf("%d ",ans); for(int i=1;i<=ans;i++) printf("%d ",pos[i]); printf(" "); return 0; }