【BZOJ】4259: 残缺的字符串 FFT

【题意】给定长度为m的匹配串B和长度为n的模板串A,求B在A中出现多少次。字符串仅由小写字母和通配符" * "组成,其中通配符可以充当任意一个字符。n<=3*10^5。

【算法】FFT

【题解】假设模板串的数组A用0~26代表所有字符,0为通配符,匹配串的数组B同理,那么用表示差异的经典套路:

$$C_n=\sum_{i=0}^{m-1}(A_{n+i}-B_i)^2*A_{n+i}*B_i$$

那么可以看出$C_n=0$当且仅当$S_A[n,n+m-1]=S_B[0,m-1]$。这里的通配符为0,所以当含有通配符时式子直接为0,即无差异。

将数组A反转,得到:

$$C_x=\sum_{i=0}^{m-1}(A'_{n-x-i-1}-B_i)^2*A'_{n-x-i-1}*B_i$$

尝试扩展上届:

$$C_x=D_{n-x-1}=\sum_{i=0}^{n-x-1}(A'_{n-x-i-1}-B_i)^2*A'_{n-x-i-1}*B_i$$

现在只需要计算Dx了,二项式拆分得到:

$$D_x=\sum_{i=0}^{x}A_{x-i}^3B_i-2A_{x-i}^2B_i^2+A_{x-i}B_i^3$$

(这里公式不知道A后为什么不能加单引号,不然latex会出错)

复杂度O(n log n)。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=1100010,N=300010;
const double PI=acos(-1);
int m,n,aa[N],bb[N],d[N];
char A[N],B[N];
struct cp{
    double x,y;
    cp(double a,double b){x=a;y=b;}
    cp(){x=y=0;}
    cp operator + (cp a){return cp(x+a.x,y+a.y);}
    cp operator - (cp a){return cp(x-a.x,y-a.y);}
    cp operator * (cp a){return cp(x*a.x-y*a.y,x*a.y+y*a.x);}
}A1[maxn],A2[maxn],A3[maxn],B1[maxn],B2[maxn],B3[maxn];
void fft(cp *a,int n,int f){
    int k=0;
    for(int i=0;i<n;i++){
        if(i<k)swap(a[i],a[k]);
        for(int j=n>>1;(k^=j)<j;j>>=1);
    }
    for(int l=2;l<=n;l<<=1){
        int m=l/2;
        cp wn(cos(2*PI*f/l),sin(2*PI*f/l));
        for(cp *p=a;p!=a+n;p+=l){
            cp w(1,0);
            for(int i=0;i<l/2;i++){
                cp t=w*p[i+m];
                p[i+m]=p[i]-t;
                p[i]=p[i]+t;
                w=w*wn;
            }
        }
    }
    if(f==-1)for(int i=0;i<n;i++)a[i].x/=n;
}
int main(){
    scanf("%d%d%s%s",&m,&n,B,A);
    for(int i=0;i<n;i++)aa[n-i-1]=(A[i]=='*'?0:A[i]-'a'+1);
    for(int i=0;i<m;i++)bb[i]=(B[i]=='*'?0:B[i]-'a'+1);
    for(int i=0;i<n;i++)A1[i]=cp(aa[i],0),A2[i]=cp(aa[i]*aa[i],0),A3[i]=cp(aa[i]*aa[i]*aa[i],0);
    for(int i=0;i<m;i++)B1[i]=cp(bb[i],0),B2[i]=cp(bb[i]*bb[i],0),B3[i]=cp(bb[i]*bb[i]*bb[i],0);
    int N=1;while(N<n+m)N<<=1;
    fft(A1,N,1);fft(B1,N,1);
    fft(A2,N,1);fft(B2,N,1);
    fft(A3,N,1);fft(B3,N,1);
    for(int i=0;i<N;i++)A1[i]=A3[i]*B1[i]-cp(2,0)*A2[i]*B2[i]+A1[i]*B3[i];
    fft(A1,N,-1);
    int cnt=0;
    for(int i=0;i<=n-m;i++)if(!((int)(A1[n-1-i].x+0.1)))d[++cnt]=i+1;
    printf("%d\n",cnt);
    for(int i=1;i<=cnt;i++)printf("%d ",d[i]);
    return 0;
}
View Code

 

posted @ 2018-04-16 19:37  ONION_CYC  阅读(...)  评论(...编辑  收藏