「BZOJ 4259」残缺的字符串

很久很久以前,在你刚刚学习字符串匹配的时候,有两个仅包含小写字母的字符串 AABB ,其中 AA 串长度为 mmBB 串长度为 nn 。可当你现在再次碰到这两个串时,这两个串已经老化了,每个串都有不同程度的残缺。

你想对这两个串重新进行匹配,其中 AA 为模板串,那么现在问题来了,请回答,对于 BB 的每一个位置 ii ,从这个位置开始连续 mm 个字符形成的子串是否可能与 AA 串完全匹配?

Constraints

1m,n3000001\leq m,n \leq 300000

Solution

首先将 * 号的值置为 00 ,那么对于两个长度皆为 mm 的串,若 i=0m1(A[i]B[i])2A[i]B[i]=0\sum _{i=0}^{m-1}(A[i]-B[i])^2 A[i]B[i]=0 ,则两个串可以完全匹配。

此时若将 AA 串翻转,并枚举在 BB 串中的结尾位置,则可得:

f[i]=j=0i(A[j]B[ij])2A[j]B[ij]=j=0iA[j]3B[ij]2j=0iA[j]2B[ij]2+j=0iA[j]B[ij]3\begin{aligned} f[i] &= \sum _{j=0}^{i}(A[j]-B[i-j])^2 A[j]B[i-j] \\ &= \sum _{j=0}^{i}A[j]^3B[i-j]-2\sum _{j=0}^{i}A[j]^2B[i-j]^2+\sum _{j=0}^{i}A[j]B[i-j]^3 \end{aligned}

直接 FFT 即可,注意卡常。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=524288+5;
const double pi=acos(-1);
int n,n1,n2,cnt,out[N],a[N],b[N];
char s1[N],s2[N];
struct cpx{double r,i;cpx(double _r=0,double _i=0):r(_r),i(_i){};};
cpx A[N],B[N],ans[N];
cpx operator + (cpx a,cpx b){return cpx(a.r+b.r,a.i+b.i);}
cpx operator - (cpx a,cpx b){return cpx(a.r-b.r,a.i-b.i);}
cpx operator * (cpx a,cpx b){return cpx(a.r*b.r-a.i*b.i,a.r*b.i+a.i*b.r);}
void fft(cpx *a,int n,int f)
{
int k=0;while((1<<k)<n)k++;
for(int i=0;i<n;i++)
{
int t=0;
for(int j=0;j<k;j++)
if(i&(1<<j))t|=(1<<(k-j-1));
if(i<t)swap(a[i],a[t]);
}
for(int l=2;l<=n;l<<=1)
{
int m=l>>1;
cpx nw=cpx(cos(2*pi/l),sin(2*pi/l)*f);
for(cpx *p=a;p!=a+n;p+=l)
{
cpx w=cpx(1,0);
for(int i=0;i<m;i++)
{
cpx t=p[m+i]*w;w=w*nw;
p[m+i]=p[i]-t;p[i]=p[i]+t;
}
}
}
if(f==-1)for(int i=0;i<n;i++)a[i].r/=n;
}
int main()
{
scanf("%d%d%s%s",&n1,&n2,s1,s2);
n=1;while(n<n1||n<n2)n<<=1;
for(int i=0;i<n1;i++)if(s1[i]!='*')a[n1-i-1]=s1[i]-'a'+1;
for(int i=0;i<n2;i++)if(s2[i]!='*')b[i]=s2[i]-'a'+1;
for(int i=0;i<n;i++)A[i]=cpx(a[i]*a[i]*a[i],0);
for(int i=0;i<n;i++)B[i]=cpx(b[i],0);
fft(A,n,1);fft(B,n,1);
for(int i=0;i<n;i++)ans[i]=A[i]*B[i];
for(int i=0;i<n;i++)A[i]=cpx(a[i]*a[i],0);
for(int i=0;i<n;i++)B[i]=cpx(b[i]*b[i],0);
fft(A,n,1);fft(B,n,1);
for(int i=0;i<n;i++)ans[i]=ans[i]-A[i]*B[i]*cpx(2,0);
for(int i=0;i<n;i++)A[i]=cpx(a[i],0);
for(int i=0;i<n;i++)B[i]=cpx(b[i]*b[i]*b[i],0);
fft(A,n,1);fft(B,n,1);
for(int i=0;i<n;i++)ans[i]=ans[i]+A[i]*B[i];
fft(ans,n,-1);
for(int i=n1-1;i<n2;i++)
if(ans[i].r<0.5)out[++cnt]=i-n1+2;
printf("%d\n",cnt);
for(int i=1;i<=cnt;i++)printf("%d ",out[i]);
return 0;
}