【BZOJ4259】殘缺的字符串
【BZOJ4259】殘缺的字符串
Description
很久很久以前,在你剛剛學習字符串匹配的時候,有兩個僅包含小寫字母的字符串A和B,其中A串長度為m,B串長度為n。可當你現在再次碰到這兩個串時,這兩個串已經老化了,每個串都有不同程度的殘缺。
你想對這兩個串重新進行匹配,其中A為模板串,那麽現在問題來了,請回答,對於B的每一個位置i,從這個位置開始連續m個字符形成的子串是否可能與A串完全匹配?
Input
第一行包含兩個正整數m,n(1<=m<=n<=300000),分別表示A串和B串的長度。
第二行為一個長度為m的字符串A。
第三行為一個長度為n的字符串B。
兩個串均僅由小寫字母和號組成,其中
Output
第一行包含一個整數k,表示B串中可以完全匹配A串的位置個數。
若k>0,則第二行輸出k個正整數,從小到大依次輸出每個可以匹配的開頭位置(下標從1開始)。
Sample Input
3 7
ab
aebrob
Sample Output
2
1 5
首先帶通配符的字符串匹配好像不能\(kmp\)。
這是\(NTT/FFT\)的一個經典應用。
如果\(A[i]與B[j]\)不能匹配,那麽\(j-i+1\)就不能作為匹配的開頭位置。
所以我們設一個函數\(\displaystyle GG(x)=\sum_{i=1}^n\sum_{j=1}^m[j-i+1==x]\cdot [A[i]與B[j]不能匹配]\)
我們將通配符位置的值設為0,其他的設為其在字符表中的序號。然後
\[
\begin{align}
\displaystyle GG(x)&=\sum_{i=1}^n\sum_{j=1}^m[j-i+1==x]\cdot (A[i]-B[j])^2A[i]B[j]\&=\sum_{i=1}^n\sum_{j=1}^m[j-i+1==x]\cdot(A[i]^3B[j]-2A[i]^2B[j]^2+A[i]B[j]^3)
\end{align}
\]
然後我們做3次FFT就可以了。
代碼:
#include<bits/stdc++.h>
#define ll long long
#define N 300005
#define Z complex<double>
#define pi acos(-1)
#define mod 998244353
using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
int n,m;
char s[N],t[N];
int x[N],y[N];
Z f[N<<2],g[N<<2];
int rev[N<<2];
void FFT(Z *a,int d,int flag) {
int n=1<<d;
for(int i=0;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<d-1);
for(int i=0;i<n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int s=1;s<=d;s++) {
int len=1<<s,mid=len>>1;
Z w(cos(2*pi*flag/len),sin(2*pi*flag/len));
for(int i=0;i<n;i+=len) {
Z t(1,0);
for(int j=0;j<mid;j++,t*=w) {
Z u=a[i+j],v=t*a[i+j+mid];
a[i+j]=u+v;
a[i+j+mid]=u-v;
}
}
}
if(flag==-1) for(int i=0;i<n;i++) a[i]/=n;
}
int Match[N<<2];
void solve(int d,int flag) {
FFT(f,d,1),FFT(g,d,1);
for(int i=0;i<(1<<d);i++) f[i]*=g[i];
FFT(f,d,-1);
for(int i=0;i<(1<<d);i++) Match[i]+=flag*(ll)(f[i].real()+0.5);
}
vector<int>ans;
ll cal2(ll a) {return a*a;}
ll cal3(ll a) {return a*a*a;}
int main() {
n=Get(),m=Get();
scanf("%s",s);
scanf("%s",t);
reverse(s,s+n);
for(int i=0;i<n;i++) x[i]=s[i]=='*'?0:s[i]-'a'+1;
for(int i=0;i<m;i++) y[i]=t[i]=='*'?0:t[i]-'a'+1;
int d=ceil(log2(m+n));
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
for(int i=0;i<n;i++) f[i]=Z(cal3(x[i]),0);
for(int i=0;i<m;i++) g[i]=Z(y[i],0);
solve(d,1);
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
for(int i=0;i<n;i++) f[i]=Z(x[i],0);
for(int i=0;i<m;i++) g[i]=Z(cal3(y[i]),0);
solve(d,1);
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
for(int i=0;i<n;i++) f[i]=Z(cal2(x[i]),0);
for(int i=0;i<m;i++) g[i]=Z(cal2(y[i]),0);
solve(d,-2);
for(int i=0;i<m+n;i++) if(Match[i]==0&&1<=i-n+2&&i-n+2<=m-n+1) ans.push_back(i-n+2);
cout<<ans.size()<<"\n";
for(int i=0;i<ans.size();i++) cout<<ans[i]<<" ";
return 0;
}
【BZOJ4259】殘缺的字符串