【BZOJ4259】殘缺的字串
題面
1684 -- 【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
a*b
aebr*ob
Sample Output
2
1 5
題目分析
法一:
我們可以參考【BZOJ3160】萬徑人蹤滅的做法,相當於求\(26\)組FFT,若和==長度即可。
然而,這樣做相當於\(O(26n\log n)\),無法在1s內完成。
法二:
還是考慮FFT,我們能否改變狀態,讓他用更少的次數求出答案?
如果\(a_i,b_j\)要配上,要麼\(a_i==b_j\),要麼\(a_i==*||b_j==*\)。
當\(a_i==b_j\),有\((a_i-b_j)\)=0,
而當\(a_i==*||b_j==*\),我們可以把\(*\)的值設為\(0\),所以有\(\displaystyle\sum_{i=1}^m(a_i-b_{j+i})\cdot a_i\cdot b_{j+i}==0\),
但是,我們要考慮,\(a_i-b_{j+i}\)可正可負,
為了防止恰好和為\(0\),我們寫作\(\displaystyle\sum_{i=1}^m(a_i-b_{j+i})^2\cdot a_i\cdot b_{j+i}==0\)
將\(b\)反轉,可以寫作與法一相似的卷積的形式,然後分別求解拼起來即可。
程式碼實現
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<iomanip>
#include<cstdlib>
#include<complex>
#define MAXN 0x7fffffff
typedef long long LL;
const int N=1100005;
using namespace std;
inline int Getint(){register int x=0,f=1;register char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}return x*f;}
typedef complex<double>Z;
const double pi=M_PI;
void FFT(Z *a,int x,int K){
static int rev[N],lst;
int n=1<<x;
if(n!=lst){
for(int i=0;i<n;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<x-1);
lst=n;
}
for(int i=0;i<n;i++)if(i<rev[i])swap(a[i],a[rev[i]]);
for(int i=1;i<n;i<<=1){
int tmp=i<<1;
Z wn(cos(pi/i),sin(pi*K/i));
for(int j=0;j<n;j+=tmp){
Z w(1,0);
for(int k=0;k<i;k++,w=w*wn){
Z x=a[j+k],y=w*a[i+j+k];
a[j+k]=x+y;a[i+j+k]=x-y;
}
}
}
if(K==-1)for(int i=0;i<n;i++)a[i]/=n;
}
Z a[3][N],b[3][N],ans[N];
char A[N],B[N];
LL Get(Z x){return (LL)(x.real()+0.5);}
int st[N],top;
int main(){
int m=Getint(),n=Getint();
scanf("%s%s",A+1,B+1);
reverse(A+1,A+1+m);
for(int i=1;i<=m;i++){
int x=(isalpha(A[i])?(A[i]-'a'+1):0);
a[0][i].real()=x,a[1][i].real()=x*x,a[2][i].real()=x*x*x;
}
for(int i=1;i<=n;i++){
int x=(isalpha(B[i])?(B[i]-'a'+1):0);
b[0][i].real()=x,b[1][i].real()=x*x,b[2][i].real()=x*x*x;
}
int x=ceil(log2(m+n+3));
FFT(a[0],x,1),FFT(a[1],x,1),FFT(a[2],x,1);
FFT(b[0],x,1),FFT(b[1],x,1),FFT(b[2],x,1);
for(int i=0;i<(1<<x);i++)
ans[i]=a[0][i]*b[2][i]+a[2][i]*b[0][i]-Z(2,0)*a[1][i]*b[1][i];
FFT(ans,x,-1);
for(int i=m+1;i<=n+1;i++)if(!Get(ans[i]))st[++top]=i-m;
cout<<top<<'\n';
for(int i=1;i<=top;i++)cout<<st[i]<<' ';
return 0;
}