【传送门:BZOJ4259】
简要题意:
给出两个字符串,第一个串长度为m,第二个串长度为n,字符串中如果有*字符,则代表当前位置可以匹配任何字符
求出第一个字符串在第二个字串中出现的次数,及出现的位置开头在第二个字符串的位置(从小到大输出)
题解:
FFT,通配符匹配
对于两个串长度为i,它们的相似程度为$\sum_{j=0}^{i-1}(A[j]-B[j])^2$(A[j]!='*'&&B[j]!='*')
把*设为0,则得到$\sum_{j=0}^{i-1}(A[j]-B[j])^2A[j]B[j]$
显然只有当$\sum_{j=0}^{i-1}(A[j]-B[j])^2A[j]B[j]$为0时,A串和B串才能完全匹配
那么对于这道题而言,设f(i)为以B的i位置为结尾的长度为n的子串与A串的相似程度
先将n--,m--(方便写公式),然后在A后面补0
显然$f(i)=\sum_{j=0}^{m}(A[j]-B[i-m+j])^2A[j]B[i-m+j]$
我们把A数组翻转,就会得到$f(i)=\sum_{j=0}^{i}(A[j]-B[i-j])^2A[j]B[i-j]$
然后把这个式子拆开就得到$f(i)=\sum_{j=0}^{i}A[j]^3B[i-j]-2*\sum_{j=0}^{i}A[j]^2B[i-j]^2+\sum_{j=0}^{i}A[j]*B[i-j]^3$
皆大欢喜,直接三次FFT分别求就可以了
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; 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[1300000],b[1300000]; int R[1300000]; 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; } void calc(int n,int m) { int L=0;m+=n; for(n=1;n<=m;n<<=1) L++; memset(R,0,sizeof(R)); for(int i=0;i<n;i++) R[i]=(R[i>>1]>>1)|(i&1)<<(L-1); fft(a,n,1);fft(b,n,1); for(int i=0;i<=n;i++) a[i]=a[i]*b[i]; fft(a,n,-1); } char s1[310000],s2[310000]; int A[310000],B[310000]; int q[310000]; double f[310000]; int main() { int n,m; scanf("%d%d",&n,&m);n--;m--; scanf("%s%s",s1,s2); for(int i=0;i<=n;i++) { if(s1[n-i]=='*') A[i]=0; else A[i]=s1[n-i]-'a'+1; } for(int i=0;i<=m;i++) { if(s2[i]=='*') B[i]=0; else B[i]=s2[i]-'a'+1; } memset(f,0,sizeof(f)); for(int i=0;i<=n;i++) a[i].r=A[i]*A[i]*A[i]; for(int i=0;i<=m;i++) b[i].r=B[i]; calc(n,m); for(int i=0;i<=m;i++) f[i]+=a[i].r; memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); for(int i=0;i<=n;i++) a[i].r=A[i]*A[i]; for(int i=0;i<=m;i++) b[i].r=B[i]*B[i]; calc(n,m); for(int i=0;i<=m;i++) f[i]-=2.0*a[i].r; memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); for(int i=0;i<=n;i++) a[i].r=A[i]; for(int i=0;i<=m;i++) b[i].r=B[i]*B[i]*B[i]; calc(n,m); for(int i=0;i<=m;i++) f[i]+=a[i].r; int cnt=0; for(int i=n;i<=m;i++) if(f[i]<0.5) q[++cnt]=i-n; printf("%d\n",cnt); if(cnt>0) { for(int i=1;i<cnt;i++) printf("%d ",q[i]+1); printf("%d\n",q[cnt]+1); } return 0; }