BZOJ2459:殘缺的字符串-FFT

權限題

題意:

有兩個僅包含小寫字母的字符串A和B,其中A串長度爲m,B串長度爲n。這兩個串已經老化了,每個串都有不同程度的殘缺。

你想對這兩個串重新進行匹配,其中A爲模板串,那麼現在問題來了,請回答,對於B的每一個位置i,從這個位置開始連續m個字符形成的子串是否可能與A串完全匹配?

1<=m<=n<=300000

Solution:

神奇的FFT解決字符串問題….

我們把 看成0,定義一個兩個長度相等的串之間的距離爲:

dis(a,b)=i=0len1(a[i]b[i])2a[i]b[i]

這樣的話如果dis(a,b)==0 ,那就說明a和b相等

爲什麼不能用dis(a,b)=i=1len(a[i]b[i])a[i]b[i] 呢?因爲這樣可能出現一個正數一個負數相加正好=0的情況

我們定義f[i]dis(a,b[im+1,i])

那麼f[i]=j=0m1(a[j]b[j+im+1])2a[j]b[j+im+1]

發現這個東西很像多項式乘法的樣子

我們翻轉A串,並在後面補0至與B串等長

那麼f[i]變爲:f[i]=j=0i(a[j]b[ij])2a[j]b[ij]

=j=0i(a[j]22a[j]b[ij]+b[ij]2)a[j]b[ij]

=j=0i(a[j]3b[ij]2a[j]2b[ij]2+a[j]b[ij]3)

然後做3遍FFT就好啦

代碼:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int N=1048580;
int n,m,len;
char s1[N],s2[N];
const double pi=acos(-1);
struct complex{
    double x,y;
    complex(double _x=0.0,double _y=0.0)
    {
        x=_x,y=_y;
    }
    complex operator +(const complex &b)const
    {
        return complex(x+b.x,y+b.y);
    }
    complex operator -(const complex &b)const
    {
        return complex(x-b.x,y-b.y);
    }
    complex operator *(const complex &b)const
    {
        return complex(x*b.x-y*b.y,y*b.x+x*b.y);
    }
}a[N],b[N],c[N];
void change(complex y[],int len)
{
    int i,j,k;
    for (i=1,j=len/2;i<len-1;i++)
    {
        if (i<j) swap(y[i],y[j]);
        k=len/2;
        while (j>=k) j-=k,k>>=1;
        if (j<k) j+=k;
    }
}
void fft(complex y[],int len,int ifi)
{
    change(y,len);
    for (int h=2;h<=len;h<<=1)
    {
        complex wn(cos(2*ifi*pi/h),sin(2*ifi*pi/h));
        for (int i=0;i<len;i+=h)
        {
            complex w(1,0);
            for (int j=i;j<i+h/2;j++)
            {
                complex u=y[j];
                complex v=w*y[j+h/2];
                y[j]=u+v;y[j+h/2]=u-v;
                w=w*wn;
            } 
        }
    }
    if (ifi==-1) for (int i=0;i<len;i++) y[i].x/=len;
}
int main()
{
    scanf("%d%d",&n,&m);
    scanf("%s",s1);scanf("%s",s2);
    for (int l=0,r=n-1;l<r;l++,r--) swap(s1[l],s1[r]);
    len=1;
    while (len<2*m) len<<=1;
    for (int i=0;i<n;i++) if (s1[i]=='*') a[i]=complex(0,0);else a[i]=complex((s1[i]-'a'+1)*(s1[i]-'a'+1)*(s1[i]-'a'+1),0);
    for (int i=n;i<len;i++) a[i]=complex(0,0);
    for (int i=0;i<m;i++) if (s2[i]=='*') b[i]=complex(0,0);else b[i]=complex((s2[i]-'a'+1),0);
    for (int i=m;i<len;i++) b[i]=complex(0,0);
    fft(a,len,1);fft(b,len,1);for (int i=0;i<len;i++) c[i]=c[i]+a[i]*b[i];

    for (int i=0;i<n;i++) if (s1[i]=='*') a[i]=complex(0,0);else a[i]=complex((s1[i]-'a'+1)*(s1[i]-'a'+1),0);
    for (int i=n;i<len;i++) a[i]=complex(0,0);
    for (int i=0;i<m;i++) if (s2[i]=='*') b[i]=complex(0,0);else b[i]=complex((s2[i]-'a'+1)*(s2[i]-'a'+1),0);
    for (int i=m;i<len;i++) b[i]=complex(0,0);
    fft(a,len,1);fft(b,len,1);for (int i=0;i<len;i++) c[i]=c[i]-complex(2,0)*a[i]*b[i];

    for (int i=0;i<n;i++) if (s1[i]=='*') a[i]=complex(0,0);else a[i]=complex((s1[i]-'a'+1),0);
    for (int i=n;i<len;i++) a[i]=complex(0,0);
    for (int i=0;i<m;i++) if (s2[i]=='*') b[i]=complex(0,0);else b[i]=complex((s2[i]-'a'+1)*(s2[i]-'a'+1)*(s2[i]-'a'+1),0);
    for (int i=m;i<len;i++) b[i]=complex(0,0);
    fft(a,len,1);fft(b,len,1);for (int i=0;i<len;i++) c[i]=c[i]+a[i]*b[i];

    fft(c,len,-1);

    int ans=0;
    for (int i=n-1;i<m;i++) 
        if (c[i].x<0.5) ans++;
    printf("%d\n",ans);
    for (int i=n-1;i<m;i++)
        if (c[i].x<0.5) printf("%d ",i-n+2);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章