bzoj4503 兩個串(FFT好題)

bzoj4503 兩個串

原題地址http://www.lydsy.com/JudgeOnline/problem.php?id=4503

題意:
兔子們在玩兩個串的遊戲。給定兩個字符串S和T,兔子們想知道T在S中出現了幾次,
分別在哪些位置出現。注意T中可能有“?”字符,這個字符可以匹配任何字符。

數據範圍
S 長度不超過 10^5, T 長度不會超過 S。 S 中只包含小寫字母, T中只包含小寫字母和“?”

題解:

最開始想的是每個字符跑一遍,26常數果斷T。

其實只需要把? 當作0,ti(siti)2 就可以看是不是相同。
那麼能匹配,就是從這一位往後len(T)位的上式的和爲0:
fi=j=0|T|1tj(si+jtj)2
然後把S串倒過來就可以化爲卷積的形式:
fi=j=0|T|1tj(sijtj)2
就是fi=j=0|T|1tj(sijtj)2=j=0|T|1s2ijtj2sijt2j+t3j
分別算出來,最後fi=0 的位就是匹配上了。

代碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int N=100005;
const int MXN=262144+1000;
const long double Pi=acos(-1);
struct Virt
{
    long double r,i;
    Virt(){}
    Virt(long double r,long double i):r(r),i(i){}
    Virt operator+(const Virt &A){return Virt(r+A.r,i+A.i);}
    Virt operator-(const Virt &A){return Virt(r-A.r,i-A.i);}
    Virt operator*(const Virt &A){return Virt(r*A.r-i*A.i,r*A.i+i*A.r);}
}omg[MXN],_omg[MXN],a[MXN],b[MXN],c[MXN];
int f[MXN],g[MXN],R[MXN],S,T,len,p;
char s[N],t[N];
void FFT(Virt *x,int opt)
{
    for(int i=0;i<len;i++) if(i<R[i]) swap(x[i],x[R[i]]);
    Virt *w; if(opt==1) w=omg; else w=_omg;
    for(int m=2;m<=len;m<<=1)
    {
        int l=m>>1;
        for(int j=0;j<len;j+=m)
        for(int i=0;i<l;i++)
        {
            Virt y=w[len/m*i]*x[i+j+l];
            x[i+j+l]=x[i+j]-y;
            x[i+j]=x[i+j]+y;
        }
    }
    if(opt==-1) for(int i=0;i<len;i++) x[i].r=(long double)x[i].r/(long double)len;
}
int main()
{
    scanf("%s%s",s,t); S=strlen(s); T=strlen(t);
    for(int i=0;i<S;i++) f[S-i-1]=(s[i]-'a')+1;
    for(int i=0;i<T;i++) g[i]=(t[i]=='?')?0:t[i]-'a'+1;
    for(len=1,p=0;len<2*S;len<<=1,p++);
    R[0]=0; for(int i=1;i<len;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(p-1));
    for(int i=0;i<len;i++) omg[i]=Virt(cos((long double)2*Pi/(long double)len*i),sin((long double)2*Pi/(long double)len*i)),_omg[i]=Virt(omg[i].r,-omg[i].i);
    for(int i=0;i<len;i++) a[i]=Virt(f[i]*f[i],0),b[i]=Virt(g[i],0);
    FFT(a,1); FFT(b,1);
    for(int i=0;i<len;i++) c[i]=a[i]*b[i];
    for(int i=0;i<len;i++) a[i]=Virt(1,0),b[i]=Virt(g[i]*g[i]*g[i],0);
    FFT(a,1); FFT(b,1);
    for(int i=0;i<len;i++) c[i]=c[i]+a[i]*b[i];
    for(int i=0;i<len;i++) a[i]=Virt((long double)2*f[i],0),b[i]=Virt((long double)g[i]*g[i],0);
    FFT(a,1); FFT(b,1);
    for(int i=0;i<len;i++) c[i]=c[i]-a[i]*b[i];
    FFT(c,-1);
    int cnt=0; for(int i=T-1;i<S;i++) if(((int)(c[i].r+0.5))==0) cnt++;
    printf("%d\n",cnt);
    for(int i=S-1;i>=T-1;i--) if(((int)(c[i].r+0.5))==0) printf("%d\n",S-i-1);
    return 0;
}
發佈了203 篇原創文章 · 獲贊 43 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章