Problem
给出长度为 m m m 的串 A A A 和长度为 n n n 的串 B B B( A A A 为模板串), ∗ * ∗ 号表示这个位置可以是任意字母。对于字符串 B B B,求出每个可以与 A A A 匹配的开头位置(下标从 1 1 1 开始)。
数据范围: 1 ≤ m ≤ n ≤ 300000 1\le m\le n\le300000 1≤m≤n≤300000。
Solution
这个 ∗ * ∗ 限制了我们的字符串算法。。。
假设 B B B 从第 j j j 位开始与 A A A 匹配,定义 f j = ∑ i = 0 m − 1 ( A i − B i + j ) 2 A i B i + j f_j=\sum_{i=0}^{m-1}(A_i-B_{i+j})^2A_iB_{i+j} fj=∑i=0m−1(Ai−Bi+j)2AiBi+j,然后令 ∗ * ∗ 的权值为 0 0 0。
发现当 A , B A,B A,B 能匹配的情况只有当 f j = 0 f_j=0 fj=0 时。
仔细想一想, ( A i − B i ) 2 = 0 (A_i-B_i)^2=0 (Ai−Bi)2=0 就是 A i = B i A_i=B_i Ai=Bi 的情况, A i = 0 A_i=0 Ai=0 就是 A A A 中有 ∗ * ∗, B i = 0 B_i=0 Bi=0 同理。
那么一样的套路,我们把 A A A 翻转一下,把下标扩展到 n n n,于是就有:
f j = ∑ i = 0 n − 1 ( A n − i + 1 − B i + j ) 2 A n − i + 1 B i + j f_j=\sum_{i=0}^{n-1}(A_{n-i+1}-B_{i+j})^2A_{n-i+1}B_{i+j} fj=i=0∑n−1(An−i+1−Bi+j)2An−i+1Bi+j
发现下标之和为 n − j + 1 n-j+1 n−j+1,在 j j j 一定时为定值(即为卷积的形式)。
把它展开:
f j = ∑ i = 0 n − 1 A n − i + 1 3 B i + j − 2 ∑ i = 0 n − 1 A n − i + 1 2 B i + j 2 + ∑ i = 0 n − 1 A n − i + 1 B i + j 3 f_j=\sum_{i=0}^{n-1}A_{n-i+1}^3B_{i+j}-2\sum_{i=0}^{n-1}A^2_{n-i+1}B^2_{i+j}+\sum_{i=0}^{n-1}A_{n-i+1}B^3_{i+j} fj=i=0∑n−1An−i+13Bi+j−2i=0∑n−1An−i+12Bi+j2+i=0∑n−1An−i+1Bi+j3
于是用 f f t fft fft,判断一下 f j f_j fj 是否为 n n n 即可。
Code
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 300005
#define ll long long
using namespace std;
const double pi=acos(-1);
int n,m,lim=1,tot;
int x[N],y[N],pos[N<<2],rec[N];
char A[N],B[N];
ll ans[N];
struct complex{
double x,y;
complex(){}
complex(double x,double y):x(x),y(y){}
friend complex operator+(const complex &a,const complex &b) {return complex(a.x+b.x,a.y+b.y);}
friend complex operator-(const complex &a,const complex &b) {return complex(a.x-b.x,a.y-b.y);}
friend complex operator*(const complex &a,const complex &b) {return complex(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
}a[N<<2],b[N<<2];
void DFT(complex f[],int type){
for(int i=0;i<lim;++i)
if(pos[i]>i) swap(f[i],f[pos[i]]);
for(int mid=1;mid<lim;mid<<=1){
complex now=complex(cos(pi/mid),type*sin(pi/mid));
for(int i=0;i<lim;i+=(mid<<1)){
complex w=complex(1,0);
for(int j=0;j<mid;++j,w=w*now){
complex p0=f[i+j],p1=w*f[i+j+mid];
f[i+j]=p0+p1,f[i+j+mid]=p0-p1;
}
}
}
if(type==-1) for(int i=0;i<lim;++i) f[i].x/=lim;
}
void FFT(int k){
DFT(a,1),DFT(b,1);
for(int i=0;i<lim;++i) a[i]=a[i]*b[i];
DFT(a,-1);
for(int i=0;i<n;++i) ans[i]+=k*(ll)(a[i].x+0.5);
for(int i=0;i<lim;++i) a[i].x=a[i].y=b[i].x=b[i].y=0;
}
int main(){
scanf("%d%d%s%s",&m,&n,A,B);
while(lim<=(n<<1)) lim<<=1;
for(int i=0;i<lim;++i) pos[i]=(pos[i>>1]>>1)|((i&1)*(lim>>1));
for(int i=0;i<m;++i) if(A[i]!='*') x[i]=A[i]-'a'+1;
for(int i=0;i<n;++i) if(B[i]!='*') y[i]=B[i]-'a'+1;
reverse(x,x+m);
for(int i=0;i<n;++i)a[i].x=x[i]*x[i]*x[i],b[i].x=y[i];FFT(1);
for(int i=0;i<n;++i)a[i].x=x[i]*x[i],b[i].x=y[i]*y[i];FFT(-2);
for(int i=0;i<n;++i)a[i].x=x[i],b[i].x=y[i]*y[i]*y[i];FFT(1);
for(int i=m-1;i<n;++i) if(!ans[i]) rec[++tot]=i-m+2;
printf("%d\n",tot);
for(int i=1;i<=tot;++i) printf("%d ",rec[i]);
return 0;
}