Solution
題目要求:
給定兩個串,問第二個串在第一個串的哪些位置出現過
第二個串中有通配符’?’
乍一眼看上去是一個KMP?
但是因爲通配符的存在使得 數組直接報廢
當然, 也是可以的
但是自己感受那令人絕望的時間複雜度吧
考慮將其轉化爲數字串做減法
設第一個串爲 ,第二個串爲 ,長度分別爲
在沒有通配符的情況下
若兩串在某個位置匹配,則一定有
拆開
發現很好化爲卷積和的形式,只需要將 反序即可
PS:這裏的 換了一下,這是爲了卷積方便
所以將 , 卷積一下即可
現在考慮加入通配符
我們要求不管是 還是 其結果都是0
那麼將整體都乘上一個 即可
原式變爲
並且分析發現, 是不需要卷積的
因爲若匹配,則 的每一位都需要貢獻一個 ,所以直接預處理出來
代碼如下:
#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;
}