Solution
题目要求:
给定两个串,问第二个串在第一个串的哪些位置出现过
第二个串中有通配符’?’
乍一眼看上去是一个KMP?
但是因为通配符的存在使得
nxt
n
x
t
数组直接报废
当然,
DP
D
P
也是可以的
但是自己感受那令人绝望的时间复杂度吧
考虑将其转化为数字串做减法
设第一个串为
a
a
,第二个串为,长度分别为
la,lb
l
a
,
l
b
在没有通配符的情况下
若两串在某个位置匹配,则一定有
拆开
发现很好化为卷积和的形式,只需要将 b b 反序即可
PS:这里的 x x 换了一下,这是为了卷积方便
所以将, b b 卷积一下即可
现在考虑加入通配符
我们要求不管是
a[i]=b[i]
a
[
i
]
=
b
[
i
]
还是
b[i]=′?′
b
[
i
]
=
′
?
′
其结果都是0
那么将整体都乘上一个
b[i]
b
[
i
]
即可
原式变为
并且分析发现,
b[x−i]3
b
[
x
−
i
]
3
是不需要卷积的
因为若匹配,则
b
b
的每一位都需要贡献一个,所以直接预处理出来
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N =100010;
const double pi=acos(-1);
char str[N];
struct Complex {
double x;
double y;
Complex() {};
Complex(double _x,double _y) { x=_x;y=_y; }
Complex operator + (const Complex o) { return Complex(x+o.x,y+o.y); }
Complex operator - (const Complex o) { return Complex(x-o.x,y-o.y); }
Complex operator * (const Complex o) { return Complex(x*o.x-y*o.y,x*o.y+y*o.x); }
}a[N<<2],_a[N<<2],b[N<<2],_b[N<<2];
int r[N<<2];
int n,m,L,R,sum;
void fft(Complex *now,int f,int n) {
for(int i=0;i<n;++i)
if(i<r[i]) swap(now[i],now[r[i]]);
for(int i=1;i<n;i<<=1) {
Complex wn(cos(pi/i),f*sin(pi/i));//w_n^i;
for(int j=0;j<n;j+=(i<<1)) {
Complex w(1,0);
for(int k=0;k<i;++k,w=w*wn) {
Complex x=now[j+k],y=w*now[j+k+i];
now[j+k]=x+y;
now[j+k+i]=x-y;
}
}
}
if(f==-1)
for(int i=0;i<n;++i)
now[i].x/=n;
}
int main() {
scanf("%s",str);
R=n=strlen(str);
for(int i=0;i<n;++i) {
a[i].x=str[i]-'a'+1;
_a[i].x=a[i].x*a[i].x;//a^2
}
scanf("%s",str);
L=m=strlen(str);
for(int i=0;i<m;++i) {
if(str[i]!='?') b[m-i-1].x=str[i]-'a'+1;
sum+=b[m-i-1].x*b[m-i-1].x*b[m-i-1].x;//b^3
_b[m-i-1].x=b[m-i-1].x*b[m-i-1].x;//b^2
}
m+=n;
int nn=0;
for(n=1;n<=m;n<<=1) nn++;
for(int i=0;i<n;++i) { r[i]=(r[i>>1]>>1)|((i&1)<<(nn-1)); }
fft(_a,1,n);fft(b,1,n);
for(int i=0;i<=n;++i) { b[i]=_a[i]*b[i]; }
fft(b,-1,n);
//*******a^2*b
fft(a,1,n);fft(_b,1,n);
for(int i=0;i<=n;++i) { a[i]=a[i]*_b[i]; }
fft(a,-1,n);
//*******a*b^2
for(int i=0;i<=n;++i) {
a[i].x=(int)(a[i].x+0.5);
b[i].x=(int)(b[i].x+0.5);
}
int times=0;
for(int i=L-1;i<=R-1;++i)
if(b[i].x+sum-2*a[i].x==0)
++times;
printf("%d\n",times);
for(int i=L-1;i<=R-1;++i)
if(b[i].x+sum-2*a[i].x==0)
printf("%d\n",i-L+1);
return 0;
}