没想到这种字符串题居然可以用fft。。。
设有两个串S1,S2,定义S1和S2的距离为:Σ(i=1,n) (s1[i]-s2[i])^2,那么两个串相同当且仅当距离=0。那么现在S2存在通配字符'?',那么;令'?'的值为0,就可以修改一下距离为:Σ(i=1,n) s2[i]*(s1[i]-s2[i])^2。
那么把S2翻转一下就变成卷积的形式了,直接上fft即可。注意可以把多次的点值的结果并在一起最后使用一次插值即可。
AC代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #define pi acos(-1.0) #define N 300005 using namespace std; struct cpx{ double r,i; }a[N],b[N],c[N]; int n1,n2,m,f[N],g[N],pos[N]; char s1[N],s2[N]; cpx operator +(cpx x,cpx y){ x.r+=y.r; x.i+=y.i; return x; } cpx operator -(cpx x,cpx y){ x.r-=y.r; x.i-=y.i; return x; } cpx operator *(cpx x,cpx y){ cpx t; t.r=x.r*y.r-x.i*y.i; t.i=x.r*y.i+x.i*y.r; return t; } void dft(cpx *a,int flag){ int i,j,k; cpx w,wn,u,v; if (flag>0) for (i=0; i<m; i++) a[i].i=0; for (k=1; k<m; k<<=1){ wn.r=cos(pi*flag/k); wn.i=sin(pi*flag/k); for (i=0; i<m; i+=(k<<1)){ w.r=1; w.i=0; for (j=i; j<i+k; j++){ u=a[j]; v=a[j+k]*w; a[j]=u+v; a[j+k]=u-v; w=w*wn; } } } if (flag<0) for (i=0; i<m; i++) a[i].r/=m; } int main(){ scanf("%s%s",s1+1,s2+1); int i,j,k,cnt=0; n1=strlen(s1+1); n2=strlen(s2+1); for (i=1; i<=n1; i++) f[i]=s1[i]-'a'+1; for (i=1; i<=n2; i++) g[n2-i+1]=(s2[i]=='?')?0:s2[i]-'a'+1; m=n1+n2+1; for (i=1; i<m; i<<=1) cnt++; m=i; for (i=0; i<m; i++) for (k=i,j=1; j<=cnt; j++,k>>=1) pos[i]=pos[i]<<1|(k&1); for (i=0; i<m; i++){ a[pos[i]].r=f[i]*f[i]; b[pos[i]].r=g[i]; } dft(a,1); dft(b,1); for (i=0; i<m; i++) c[pos[i]]=a[i]*b[i]; for (i=0; i<m; i++){ a[pos[i]].r=1; b[pos[i]].r=g[i]*g[i]*g[i]; } dft(a,1); dft(b,1); for (i=0; i<m; i++) c[pos[i]]=c[pos[i]]+a[i]*b[i]; for (i=0; i<m; i++){ a[pos[i]].r=f[i]*2; b[pos[i]].r=g[i]*g[i]; } dft(a,1); dft(b,1); for (i=0; i<m; i++) c[pos[i]]=c[pos[i]]-a[i]*b[i]; dft(c,-1); cnt=0; for (i=1; i<=n1-n2+1; i++) if (c[i+n2].r<0.5) f[++cnt]=i; printf("%d\n",cnt); for (i=1; i<=cnt; i++) printf("%d\n",f[i]-1); return 0; }
by lych
2016.4.11