【FFT】UVALive4671 K-neighbor substrings

9 篇文章 0 订阅
2 篇文章 0 订阅

分析:

这道题巧用了卷积的性质。

使用FFT来实现字符串匹配。
是一种高效的匹配方式,但缺陷在于,其必须按照每种字符分别匹配,有多少种字符,就要匹配多少次。

如果把模式串翻转,然后依次把某一字符设为1,其他字符设为0,此时做一次卷积,得到的结果 Ax A x 就表示以x为终点匹配的字符数。

然后这道题需要字符串hash来判重,本来很经典的一道题这样一来就有点画蛇添足了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#define SF scanf
#define PF printf
#define MAXN 300000
using namespace std;
typedef long long ll;
int G=3;
int siz;
const double Pi=acos(-1);
struct cpx{
    double r,i;
    cpx() {}
    cpx(double _r,double _i):r(_r),i(_i) {}
    cpx operator * (const cpx &a) const{
        return cpx(r*a.r-i*a.i,r*a.i+i*a.r);
    }
    cpx operator + (const cpx &a) const{
        return cpx(r+a.r,i+a.i);
    }
    cpx operator - (const cpx &a) const{
        return cpx(r-a.r,i-a.i);
    }
};
void fft(cpx *a,int f,int N){
    int i,j,k;
    for(i=1,j=0;i<N;i++){
        for(int d=N;j^=d>>=1,~j&d;);
        if(i<j)
            swap(a[i],a[j]);
    }
    for(i=1;i<N;i<<=1){
        cpx wn(cos(Pi/i),f*sin(Pi/i));
        for(j=0;j<N;j+=i<<1){
            cpx w(1,0);
            for(k=0;k<i;k++,w=w*wn){
                cpx x=a[j+k],y=w*a[i+j+k];
                a[j+k]=x+y;
                a[i+j+k]=x-y;
            }
        }
    }
    if(f==-1)
        for(i=0;i<N;i++)
            a[i].r/=N;
}
cpx A[MAXN],B[MAXN];
int n,x,m,s;
int k,sum[MAXN],Cas;
char s1[MAXN],s2[MAXN];
set<ll> s3;
ll head[MAXN],seed=3,powx[MAXN];
int main(){
    powx[0]=1;
    for(int i=1;i<=100000;i++)
        powx[i]=powx[i-1]*seed;
    while(SF("%d",&k)!=EOF){
        Cas++;
        if(k==-1)
            break;
        s3.clear();
        memset(A,0,sizeof A);
        memset(B,0,sizeof B);
        SF("%s%s",s1,s2);
        int len1=strlen(s1);
        int len2=strlen(s2);
        siz=1;
        while(siz<=len1+len2)
            siz*=2;
        head[0]=(s1[0]=='a');
        for(int i=1;i<=len1;i++)
            head[i]=head[i-1]*seed+(s1[i]=='a');
        k=len2-k;
        for(int i=0;i<len1;i++)
            if(s1[i]=='a')
                A[i].r=1;
        for(int i=0;i<len2;i++)
            if(s2[i]=='a')
                B[len2-i-1].r=1;
        fft(A,1,siz);
        fft(B,1,siz);
        for(int i=0;i<siz;i++)
            A[i]=A[i]*B[i];
        fft(A,-1,siz);
        for(int i=len2-1;i<len1;i++)
            sum[i]=int(A[i].r+0.5);
        memset(A,0,sizeof A);
        memset(B,0,sizeof B);
        for(int i=0;i<len1;i++)
            if(s1[i]=='b')
                A[i].r=1;
        for(int i=0;i<len2;i++)
            if(s2[i]=='b')
                B[len2-i-1].r=1;
        fft(A,1,siz);
        fft(B,1,siz);
        for(int i=0;i<siz;i++)
            A[i]=A[i]*B[i];
        fft(A,-1,siz);
        int ans=0;
        for(int i=len2-1;i<len1;i++)
            sum[i]+=int(A[i].r+0.5);
        for(int i=len2-1;i<len1;i++){
            if(sum[i]>=k){
                ll pre;
                if(i==len2-1)
                    pre=0;
                else
                    pre=head[i-len2];
                ll sx=head[i]-pre*powx[len2];
                s3.insert(sx);
            }   
        }
        PF("Case %d: %d\n",Cas,s3.size());
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值