【BZOJ4503】兩個串(FFT)

【BZOJ4503】兩個串(FFT)

題面

給定串\(S\),以及帶通配符的串\(T\),詢問\(T\)\(S\)中出現了幾回。而且輸出對應的位置。ios

\(|S|,|T|<=10^5\),字符集大小爲\(26\)函數

題解

先來考慮沒有通配符怎麼匹配。別跟我說KMP!!spa

根據前面幾個題目的套路,咱們能夠把每一個字符分開來考慮,而後將\(T\)串反轉,將有這個字符的位置變成\(1\),而後\(FFT\),就能夠知道在這一段裏面這個字符匹配上了多少個,而後把每一個字符求個和,檢查是否剛好匹配了\(|T|\)個。code

通配符此時並不須要考慮。string

時間複雜度\(O(26nlogn)\)it

固然,若是您真的這麼寫了,那麼確定\(T\)飛了。\(FFT\)自帶巨大常數。對於字符集很小的時候上述方法是可行的,字符集很大的時候就不能這麼作了。io

因此咱們考慮如何只作一遍\(FFT\)。(沒有通配符的狀況下)class

每一個字符固然不能動了,因此把他們對應成數字。若是匹配上了怎麼辦?那就是上下兩個對應的數字相等,能夠考慮做差。可是又發現做差可能會使得幾個正數和幾個負數相加變成\(0\),因此考慮平方。stream

因此,咱們定義每一個位置的函數值\(f(x)=\sum_{i=1}^{|T|}(S[x+i-1]-T[i])^2\)map

\(T\)串反轉,平方項拆開以後,就變成了兩個卷積+一個常數項的形式,直接\(FFT\)而後求和,檢查最後的函數值是否爲\(0\)就好了。

如今有了通配符,咱們不妨令\(a..z\)對應的數字爲\(1..26\)。通配符不管給它一個什麼數字,它和上面的字符的差的平方必定不爲\(0\)

因此咱們換種方法考慮,把通配符對應的數字設爲\(0\),可是這樣差的平方不是\(0\),因此咱們就在上面那個式子後面乘上一個\(T[i]\)的權值,若是此時是通配符就會乘\(0\),從而也變成了\(0\)。這樣一來,原來的式子就變成了\(f(x)=\sum_{i=1}^{|T|}(S[x+i-1]-T[i])^2T[i]\)

拆開後是兩個卷積+一個常數項的形式,\(FFT\)便可。

時間複雜度\(O(nlogn)\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 333333
const double Pi=acos(-1);
struct Complex{double a,b;}A1[MAX],B1[MAX],A2[MAX],B2[MAX],W[MAX],F[MAX];
Complex operator+(Complex a,Complex b){return (Complex){a.a+b.a,a.b+b.b};}
Complex operator-(Complex a,Complex b){return (Complex){a.a-b.a,a.b-b.b};}
Complex operator*(Complex a,Complex b){return (Complex){a.a*b.a-a.b*b.b,a.a*b.b+a.b*b.a};}
int n,m,r[MAX],N,Z;
int pos[MAX],ans,l;
char S[MAX],T[MAX];
void FFT(Complex *P,int opt)
{
    for(int i=1;i<N;++i)if(i<r[i])swap(P[i],P[r[i]]);
    for(int i=1;i<N;i<<=1)
        for(int p=i<<1,j=0;j<N;j+=p)
            for(int k=0;k<i;++k)
            {
                Complex w=(Complex){W[N/i*k].a,W[N/i*k].b*opt};
                Complex X=P[j+k],Y=w*P[j+k+i];
                P[j+k]=X+Y;P[i+j+k]=X-Y;
            }
    if(opt==-1)for(int i=0;i<N;++i)P[i].a/=N;
}
int main()
{
    scanf("%s",S);scanf("%s",T);
    n=strlen(S);m=strlen(T);
    for(N=1;N<=(n+m-2);N<<=1)++l;
    for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
    for(int i=1;i<N;i<<=1)
        for(int k=0;k<i;++k)W[N/i*k]=(Complex){cos(k*Pi/i),sin(k*Pi/i)};
    for(int i=0;i<n;++i)A1[i].a=(S[i]-96)*(S[i]-96),A2[i].a=2*(S[i]-96);
    for(int i=0;i<m;++i)
    {
        int x=((T[m-i-1]=='?')?0:(T[m-i-1]-96));
        B1[i].a=x;B2[i].a=x*x;Z+=x*x*x;
    }
    FFT(A1,1);FFT(B1,1);FFT(A2,1);FFT(B2,1);
    for(int i=0;i<N;++i)
        F[i]=A1[i]*B1[i]-A2[i]*B2[i];
    FFT(F,-1);
    for(int i=m-1;i<n;++i)
        if((int)(F[i].a+0.5+Z)==0)pos[++ans]=i-m+1;
    printf("%d\n",ans);
    for(int i=1;i<=ans;++i)printf("%d\n",pos[i]);
    return 0;
}
相關文章
相關標籤/搜索