[BZOJ4503]两个串(快速傅立叶变换FFT)

6 篇文章 0 订阅

Description
兔子们在玩两个串的游戏。给定两个字符串S和T,兔子们想知道T在S中出现了几次,
分别在哪些位置出现。注意T中可能有“?”字符,这个字符可以匹配任何字符。

Input
两行两个字符串,分别代表S和T

Output
第一行一个正整数k,表示T在S中出现了几次
接下来k行正整数,分别代表T每次在S中出现的开始位置。按照从小到大的顺序输出,S下标从0开始。

Sample Input
bbabaababaaaaabaaaaaaaabaaabbbabaaabbabaabbbbabbbbbbabbaabbbababababbbbbbaaabaaabbbbbaabbbaabbbbabab
a?aba?abba

Sample Output
0

HINT
S 长度不超过 10^5, T 长度不会超过 S。 S 中只包含小写字母, T中只包含小写字母和“?”


这题酷似kmp,但是由于有通配符”?”的存在,所以并不能用。
那么可以把“匹配”这个概念转化为
(c1c2)2=0
因为有通配符的存在
c2(c1c2)2=0
那么子串匹配也就是求

lenT1j=0Tj(SiTj)2=0

把这个式子展开来

lenT1j=0Tj(S2i2SiTj+T2j)=0
lenT1j=0TjS2i2SiT2j+T3j=0

其实 i 可以表示成j+x那么这条式子就可以表示成

lenT1j=0TjS2j+x2Sj+xT2j+T3j=0

进而

lenT1j=0TjS2j+xlenT1j=02Sj+xT2j+lenT1j=0T3j=0

最后一个我们暂时不用去管,先看前面两个。
因为卷积满足函数的下标和不变,但是这两个是差不变,那么我们考虑把其中一个函数反转,使其满足卷积下标和不变的性质。
Ti=TlenTi1

lenT1j=0TlenTj1S2j+xlenT1j=02Sj+xT2lenTj1+lenT1j=0T3lenTj1=0

然后我们再发现
lenTj1+j+x=lenT+x1lenT1
但事实上,这是等效的,因为 lenT1 ~ lenT+x1 的这一段我们都可以把它看作0。
那么三次fft求解即可


code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;
typedef long long LL;
const int maxn=110000;
const double pi=acos(-1.0);
struct complex
{
    double r,i;
    complex() {}
    complex(double _r,double _i) {r=_r,i=_i;}
    friend complex operator + (const complex &x,const complex &y) {return complex(x.r+y.r,x.i+y.i);}
    friend complex operator - (const complex &x,const complex &y) {return complex(x.r-y.r,x.i-y.i); }
    friend complex operator * (const complex &x,const complex &y) {return complex(x.r*y.r-x.i*y.i,x.r*y.i+x.i*y.r);}
} a[maxn*4],b[maxn*4],c[maxn*4];
int R[maxn*4];
void fft(complex *y,int len,int on)
{
    for(int i=0; i<len; i++)if(i<R[i])swap(y[i],y[R[i]]);
    for(int i=1; i<len; i<<=1)
    {
        complex wn(cos(pi/i),sin(on*pi/i));
        for(int j=0; j<len; j+=(i<<1))
        {
            complex w(1,0);
            for(int k=0; k<i; k++,w=w*wn)
            {
                complex u=y[j+k];
                complex v=w*y[j+k+i];
                y[j+k]=u+v;
                y[j+k+i]=u-v;
            }
        }
    }
    if(on==-1)for(int i=0; i<len; i++)y[i].r/=len;
}
inline int get(char c)
{
    if(c>='a' && c<='z') return c-'a'+1;
    else return 0;
}
char s1[maxn*4],s2[maxn*4];
int S[maxn*4],T[maxn*4];
int main()
{
    int lenS,lenT;
    scanf("%s",s1);
    lenS=strlen(s1);
    for(int i=0; i<lenS; i++) S[i]=get(s1[i]);
    scanf("%s",s2);
    lenT=strlen(s2);
    for(int i=0; i<lenT; i++) T[i]=get(s2[lenT-i-1]);
    int m=lenS+lenT,n,L=0;
    for(n=1; n<=m; n*=2) L++;
    for(int i=0; i<n; i++) R[i]=(R[i>>1]>>1)|(i&1)<<(L-1);

    for(int i=0; i<n; i++) a[i]=complex(S[i]*S[i],0);
    for(int i=0; i<n; i++) b[i]=complex(T[i],0);
    fft(a,n,1);
    fft(b,n,1);
    for(int i=0; i<n; i++) c[i]=c[i]+a[i]*b[i];

    for(int i=0; i<n; i++) a[i]=complex(1,0);
    for(int i=0; i<n; i++) b[i]=complex(T[i]*T[i]*T[i],0);
    fft(a,n,1);
    fft(b,n,1);
    for(int i=0; i<n; i++) c[i]=c[i]+a[i]*b[i];

    for(int i=0; i<n; i++) a[i]=complex(S[i]*2,0);
    for(int i=0; i<n; i++) b[i]=complex(T[i]*T[i],0);
    fft(a,n,1);
    fft(b,n,1);
    for(int i=0; i<n; i++) c[i]=c[i]-a[i]*b[i];

    fft(c,n,-1);
    int ans=0;
    for(int i=0; i<lenS-lenT+1; i++) if(c[i+lenT-1].r<0.5) ans++;
    printf("%d\n",ans);
    for(int i=0; i<lenS-lenT+1; i++) if(c[i+lenT-1].r<0.5) printf("%d\n",i);

    return 0;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值