这不是一道扩展kmp模板题。
题意:给定两个字符串A和B,求B在A中出现了几次及每次的位置。两个串均由小写字母组成,但B串有通配符“*”,表示可以匹配任何非空字符。
B在A中出现过,就是B的每一位和A中的连续的一段对应相等。如果我们构造一个多项式:
ci=∑j=0m−1(si−n+j+1−tj)2⋅tj
c
i
=
∑
j
=
0
m
−
1
(
s
i
−
n
+
j
+
1
−
t
j
)
2
·
t
j
那么如果 ci c i 等于零,那么也就说明了A串以 i i 结束的长度为 的串和B串相同。但是发现这个式子不是很好算,我们把B倒过来,那么:
ci=∑j=0m−1(si−j−tj)2⋅tj
c
i
=
∑
j
=
0
m
−
1
(
s
i
−
j
−
t
j
)
2
·
t
j
然后把平方展开,发现可以用FFT啦。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define cp comp
using namespace std;
struct comp{
double x,y;
}h1[400010],h2[400010],g1[400010],g2[400010],ans[400010];
const double pi=acos(-1);
double aa[400010],bb[400010];
char s[100010],t[100010];
int rev[400010],len=1,bit,ksj[400010];
inline cp operator + (cp a,cp b)
{
cp ans;
ans.x=a.x+b.x;ans.y=a.y+b.y;
return ans;
}
inline cp operator - (cp a,cp b)
{
cp ans;
ans.x=a.x-b.x;ans.y=a.y-b.y;
return ans;
}
inline cp operator * (cp a,cp b)
{
cp ans;
ans.x=a.x*b.x-a.y*b.y;ans.y=a.x*b.y+a.y*b.x;
return ans;
}
inline void FFT(cp *a,int t)
{
for(int i=0;i<len;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int step=1;step<len;step<<=1)
{
cp wn={cos(2*pi/step/2),sin(2*pi*t/step/2)};
for(int j=0;j<len;j+=(step<<1))
{
cp wnk={1,0};
for(int k=j;k<j+step;k++)
{
cp x=a[k],y=wnk*a[k+step];
a[k]=x+y,a[k+step]=x-y;
wnk=wnk*wn;
}
}
}
if(t==-1) for(int i=0;i<len;i++) a[i].x/=len;
}
int main()
{
scanf("%s%s",s,t);
double tmp=0;
int n=strlen(s),m=strlen(t);
while(len<(n+m)) len<<=1,bit++;
for(int i=0;i<=len;i++)
rev[i]=(rev[i>>1]>>1)|((i&1)<<bit-1);
for(int i=0;i<n;i++) aa[i]=s[i]-'a'+1;
for(int i=0;i<m;i++) if(t[i]=='*') bb[m-i-1]=0; else bb[m-i-1]=t[i]-'a'+1;
for(int i=0;i<n;i++) h2[i].x=aa[i]*aa[i],h1[i].x=2*aa[i];
for(int i=0;i<m;i++) g1[i].x=bb[i],g2[i].x=bb[i]*bb[i],tmp+=bb[i]*bb[i]*bb[i];
FFT(h1,1),FFT(h2,1),FFT(g1,1),FFT(g2,1);
for(int i=0;i<len;i++) ans[i]=(h2[i]*g1[i])-(h1[i]*g2[i]);
FFT(ans,-1);
int cnt=0;
for(int i=0;i<len;i++) if(floor(ans[i].x+0.5+tmp)==0) ksj[++cnt]=i-m+1;
cout<<cnt<<endl;
for(int i=1;i<=cnt;i++) cout<<ksj[i]<<' ';
return 0;
}