URAL 1996 Cipher Message 3
神题。
记得当初DYF和HZA讲过一个FFT+KMP的题目,一直觉得很神,从来没去做。
没有真正理解FFT的卷积。
首先考虑暴力。
只考虑前7位 KMP 找出所有 B 串可以匹配 A 串的位置。
设 a(i) = A(i) & 1, b(i) = B(i) & 1
然后相当于求所有的
c(i) = ∑k=0m-1 a(i+k) * b(k)
考虑卷积形式:
c(i) = ∑k=0m-1 a(k) * b(i - k)
将b串反过来
c(i) = ∑k=0m-1 a(k) * b(m-i+k)
改变 c 的定义令原先的 C(i) = 现在的 c(i+m)
得到 C(i) = ∑k=0m-1 a(k) * b(k-i)
所以 C(i) = ∑k=0m a(k) * b(i-k+n)
然后FFT解决。
#include <cstdio> #include <cstring> #include <algorithm> #include <complex> #include <cmath> #define N 250010 #define Ex complex<double> #define pi 3.14159265354 using namespace std; int n,m,a[N],b[N],same[N],rev[N<<2]; inline void scan(int &x){ char tmp[11]; scanf("%s",tmp); x=0; int len=strlen(tmp); for(int i=len-1;~i;i--) if(tmp[i]=='1') x|=(1<<(len-i-1)); } inline bool simple(int a,int b){ return (a|1)==(b|1); } int f[N]; bool match[N]; Ex A[N<<2],B[N<<2],C[N<<2]; void fft(Ex x[],int n,int t){ for(int i=0;i<n;i++){ if(rev[i]>i) swap(x[rev[i]],x[i]); } for(int m=1;m<n;m<<=1){ Ex wn(cos(pi/m*t),sin(pi/m*t)); for(int k=0;k<n;k+=(m<<1)){ Ex wt(1,0); for(int i=0;i<m;i++,wt*=wn){ Ex &A=x[i+m+k],&B=x[i+k],tmp=wt*A; A=B-tmp; B=B+tmp; } } } if(t==-1){ for(int i=0;i<n;i++) x[i]/=(double)n; } } int main(){ freopen("message10.in","r",stdin); scanf("%d%d",&n,&m); for(int i=0;i<n;i++) scan(a[i]); for(int i=0;i<m;i++) scan(b[i]); f[0]=f[1]=0; for(int i=1;i<m;i++){ int j=f[i]; while(j&&!simple(b[i],b[j])) j=f[j]; f[i+1]= simple(b[i],b[j])? j+1:0; } int j=0; bool flag=0; for(int i=0;i<n;i++){ while(j&&!simple(b[j],a[i])) j=f[j]; if(simple(b[j],a[i])) j++; if(j==m) match[i-m+1]=1,flag=1; } if(!flag){ puts("No"); return 0; } puts("Yes"); int T=0,nt; for(nt=1;nt<=(n+m);nt<<=1) T++; for(int i=0;i<nt;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(T-1)); for(int i=0;i<n;i++) A[i]=a[i]&1; for(int i=0;i<m;i++) B[i]=b[m-i-1]&1; fft(A,nt,1); fft(B,nt,1); for(int i=0;i<nt;i++) C[i]=A[i]*B[i]; fft(C,nt,-1); for(int i=0;i<n;i++) same[i]+=(int)(C[i].real()+0.5); for(int i=0;i<nt;i++) A[i]=B[i]=C[i]=0; for(int i=0;i<n;i++) A[i]=(a[i]&1)^1; for(int i=0;i<m;i++) B[i]=(b[m-i-1]&1)^1; fft(A,nt,1); fft(B,nt,1); for(int i=0;i<nt;i++) C[i]=A[i]*B[i]; fft(C,nt,-1); for(int i=0;i<n;i++) same[i]+=(int)(C[i].real()+0.5); int ansv=0x3f3f3f3f,anst=0; for(int i=0;i<n;i++){ if(!match[i]) continue; if(m-same[i+m-1]<ansv){ ansv=m-same[i+m-1]; anst=i+1; } } printf("%d %d\n",ansv,anst); return 0; }