1. 程式人生 > >【BZOJ4259】殘缺的字符串

【BZOJ4259】殘缺的字符串

span 翻轉 inpu complex 發現 vector sizeof 通配符 begin

【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
aebr
ob

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]不能匹配]\)

。我們發現這個函數有點像一個卷積的形式。於是我們將第一個字符串翻轉(因為是\(-i\)),然後關鍵在於怎麽構造卷積來使得不同的字符對\(GG\)函數有貢獻。

我們將通配符位置的值設為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】殘缺的字符串