luogu4173 残缺的字符串
对于一类带有通配符的字符串匹配问题,我们考虑构造匹配函数,通过匹配函数的值来判断匹配的位置。
先考虑一个不带通配符的问题:给定两个字符串\(A,B\),判断\(B\)的哪些位置能与\(A\)匹配。
除了kmp,我们同样可以考虑构造匹配函数来解决匹配问题,首先将\(A\)串翻转同时在其末尾补\(0\),构造函数\(f_i=\sum_{j=0}^i(A_j-B_{i-j})^2\),那么\(B\)中在第\(i\)个位置结尾的长度为\(|A|\)的子串能与\(A\)匹配当且仅当\(f_i=0\)。将函数展开后得到\(\sum_{j=0}^i(A_j^2-2A_jB_{i-j}+B_{i-j}^2)\),只需要一次FFT便能得到结果。
现在考虑有通配符的情况,那么原来对匹配函数的定义显然是不完备的,考虑第\(i\)位有通配符时直接算作匹配,故可以在第\(i\)位为通配符时令\(A_i=0\),那么便可将匹配函数的定义完善为\(f_i=\sum_{j=0}^i(A_j-B_{i-j})^2A_jB_{i-j}\).
展开后得到\(f_i=\sum_{j=0}^i(A_j^3B_{i-j}-2A_j^2B_{i-j}^2+A_jB_{i-j}^3)\).
做三次多项式乘法即可。
#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<bitset>
#include<math.h>
#include<stack>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double db;
typedef pair<int,int> pii;
const int N=300000+100;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define go(u,i) for (register int i=head[u];i;i=sq[i].nxt)
#define fir first
#define sec second
#define mp make_pair
#define pb push_back
#define maxd 998244353
#define eps 1e-8
inline int read()
{
int x=0,f=1;char ch=getchar();
while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
return x*f;
}
namespace My_Math{
#define N 100000
int fac[N+100],invfac[N+100];
int add(int x,int y) {return x+y>=maxd?x+y-maxd:x+y;}
int dec(int x,int y) {return x<y?x-y+maxd:x-y;}
int mul(int x,int y) {return 1ll*x*y%maxd;}
ll qpow(ll x,int y)
{
ll ans=1;
while (y)
{
if (y&1) ans=mul(ans,x);
x=mul(x,x);y>>=1;
}
return ans;
}
int inv(int x) {return qpow(x,maxd-2);}
int math_init()
{
fac[0]=invfac[0]=1;
rep(i,1,N) fac[i]=mul(fac[i-1],i);
invfac[N]=inv(fac[N]);
per(i,N-1,1) invfac[i]=mul(invfac[i+1],i+1);
}
#undef N
}
using namespace My_Math;
namespace polynomial{
struct complex{
double x,y;
complex (double _x=0.0,double _y=0.0) {x=_x;y=_y;}
};
complex operator +(complex a,complex b) {return complex(a.x+b.x,a.y+b.y);}
complex operator -(complex a,complex b) {return complex(a.x-b.x,a.y-b.y);}
complex operator *(complex a,complex b) {return complex(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
int r[N<<2];
void calcr(int &lim,int len)
{
lim=1;int cnt=0;
while (lim<len) {lim<<=1;cnt++;}
rep(i,0,lim-1)
r[i]=(r[i>>1]>>1)|((i&1)<<(cnt-1));
}
void fft(int lim,complex *a,int typ)
{
rep(i,0,lim-1)
if (i<r[i]) swap(a[i],a[r[i]]);
for (int mid=1;mid<lim;mid<<=1)
{
complex wn=complex(cos(pi/mid),sin(pi/mid)*typ);
int len=(mid<<1);
for (int sta=0;sta<lim;sta+=len)
{
complex w=complex(1,0);
for (int j=0;j<mid;j++,w=w*wn)
{
complex x=a[j+sta],y=a[j+sta+mid]*w;
a[j+sta]=x+y;a[j+sta+mid]=x-y;
}
}
}
if (typ==-1)
rep(i,0,lim-1) a[i].x/=lim;
}
}
using namespace polynomial;
complex A[N<<2],B[N<<2],C[N<<2],emp=complex(0,0);
int a[N],b[N],n,m;
char s[N];
vector<int> ans;
int main()
{
n=read();m=read();
scanf("%s",s);
rep(i,0,n-1)
if (s[i]!='*') a[i]=s[i]-'a'+1;
reverse(a,a+n);
scanf("%s",s);
rep(i,0,m-1)
if (s[i]!='*') b[i]=s[i]-'a'+1;
int lim=0;
calcr(lim,m*2);
rep(i,0,n-1) A[i]=complex(a[i]*a[i]*a[i],0);
rep(i,0,m-1) B[i]=complex(b[i],0);
fft(lim,A,1);fft(lim,B,1);
rep(i,0,lim-1) C[i]=C[i]+A[i]*B[i];
rep(i,0,lim-1) A[i]=B[i]=emp;
rep(i,0,n-1) A[i]=complex(a[i]*a[i],0);
rep(i,0,m-1) B[i]=complex(b[i]*b[i],0);
fft(lim,A,1);fft(lim,B,1);
rep(i,0,lim-1) C[i]=C[i]-A[i]*B[i]*2;
rep(i,0,lim-1) A[i]=B[i]=emp;
rep(i,0,n-1) A[i]=complex(a[i],0);
rep(i,0,m-1) B[i]=complex(b[i]*b[i]*b[i],0);
fft(lim,A,1);fft(lim,B,1);
rep(i,0,lim-1) C[i]=C[i]+A[i]*B[i];
fft(lim,C,-1);
rep(i,n-1,m-1)
if (fabs(C[i].x)+0.5<1) ans.pb(i-n+2);
int len=ans.size();
printf("%d\n",len);
rep(i,0,len-1) printf("%d ",ans[i]);
return 0;
}