题目描述
很久很久以前,在你刚刚学习字符串匹配的时候,有两个仅包含小写字母的字符串A和B,其中A串长度为m,B串长度为n。可当你现在再次碰到这两个串时,这两个串已经老化了,每个串都有不同程度的残缺。
你想对这两个串重新进行匹配,其中A为模板串,那么现在问题来了,请回答,对于B的每一个位置i,从这个位置开始连续m个字符形成的子串是否可能与A串完全匹配?
题解
带通配符的字符串匹配问题。
我们先把通配符设为0,考虑如果匹配串中的一段和模式串完全匹配,那么必然满足∑(a[i]-b[i])^2*a[i]*b[i]=0。
很容易发现这是个卷积,那么把任意一个串倒过来FFT一下就好了。
这题还卡我精度。。。
代码
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #define N 600002 #define double long double using namespace std; typedef long long ll; int l,L,n,m,rev[N],ans[N]; char s1[N],s2[N]; const double pai=acos(-1.0); struct fs{ double x,y; fs(double xx=0,double yy=0){x=xx;y=yy;} fs operator +(const fs &b)const{return fs{x+b.x,y+b.y};} fs operator -(const fs &b)const{return fs{x-b.x,y-b.y};} fs operator *(const fs &b)const{return fs{x*b.x-y*b.y,x*b.y+y*b.x};} }a[N],b[N],c[N],d[N],e[N]; inline void FFT(fs *a,int tag){ for(int i=1;i<l;++i)if(i>rev[i])swap(a[i],a[rev[i]]); for(int i=1;i<l;i<<=1){ fs wn(cos(pai/i),tag*sin(pai/i)); for(int j=0;j<l;j+=(i<<1)){ fs w(1,0); for(int k=0;k<i;++k,w=w*wn){ fs x=a[j+k],y=a[i+j+k]*w; a[j+k]=x+y;a[i+j+k]=x-y; } } } } int main(){ scanf("%d%d",&m,&n); l=1;L=0; while(l<n)l<<=1,L++; for(int i=1;i<l;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1)); scanf("%s%s",s2,s1); for(int i=0;i<n;++i)if(s1[i]=='*')s1[i]=0;else s1[i]-='A'; for(int i=0;i<m;++i)if(s2[i]=='*')s2[i]=0;else s2[i]-='A'; reverse(s2,s2+m); for(int i=0;i<n;++i)a[i].x=1ll*s1[i]*s1[i]*s1[i]; for(int i=0;i<m;++i)b[i].x=s2[i]; FFT(a,1);FFT(b,1); for(int i=0;i<l;++i)c[i]=a[i]*b[i]; FFT(c,-1); for(int i=0;i<l;++i)c[i].x=(ll)(c[i].x/l+0.4); memset(a,0,sizeof(a));memset(b,0,sizeof(b)); for(int i=0;i<n;++i)a[i].x=s1[i]*s1[i]; for(int i=0;i<m;++i)b[i].x=s2[i]*s2[i]; FFT(a,1);FFT(b,1); for(int i=0;i<l;++i)d[i]=a[i]*b[i]*fs(2,0); FFT(d,-1); for(int i=0;i<l;++i)d[i].x=(ll)(d[i].x/l+0.4); memset(a,0,sizeof(a));memset(b,0,sizeof(b)); for(int i=0;i<n;++i)a[i].x=s1[i]; for(int i=0;i<m;++i)b[i].x=1ll*s2[i]*s2[i]*s2[i]; FFT(a,1);FFT(b,1); for(int i=0;i<l;++i)e[i]=a[i]*b[i]; FFT(e,-1); for(int i=0;i<l;++i)e[i].x=(ll)(e[i].x/l+0.4); for(int i=m-1;i<n;++i)if(c[i].x+e[i].x-d[i].x==0)ans[++ans[0]]=i-m+2; printf("%d ",ans[0]); for(int i=1;i<=ans[0];++i)printf("%d ",ans[i]); return 0; }