bzoj 4259 残缺的字符串

题目:bzoj4259 残缺的字符串
洛谷无需权限

今天刚学的fft,于是专门找的这类题;
由于知道算法是什么,就奔着fft上想了;
比较容易就能得出,如果将模板串翻转位置之后,不足的位置补零再与要匹配的串做fft,那么得出的多项式中F[ i ]是所有下标之和为i的两个字符的乘积,而由于模板串后边的位置被补零,所以能够找到n-m个F[i]表示所有匹配情况;
接下来考虑怎么判断每个情况是否合法,将模板串中26个字母都设为一个数,再将匹配串中所有字母设为倒数,如果有“*”号那么设为0,那么得出的每个表示匹配情况的值如果是个整数,就是合法的,如果不是就不合法;
当然这样有可能有精度问题,所以得给每个字符一开始的值加一个数,似乎加个1000左右的数就可以,加个100以内的可能会被卡;
至于判断是否是整数时,实测1e-6也是可以过;
总之这个做法比较玄学,但唯一的好处是只需要做一次fft,跑得比较快

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define random(a,b) (a+rand()%(b-a+1))
#define pi 3.141592653589793238460643383279
const int maxn=1048577;
int n,m;
int nn=1,tk=0;
int a[maxn],b[maxn],c[maxn];
double abs(double x)
{
    if(x<0)return -x;
    return x;
} 
struct com
{
    double r,i;
    com(){
        r=i=0.0;
    }
    com(double x,double y){
        r=x,i=y;    
    } 
    com operator +(const com &o)const{
        return com(r+o.r,i+o.i);
    }
    com operator -(const com &o)const{
        return com(r-o.r,i-o.i);
    }
    com operator *(const com &o)const{
        return com(r*o.r-i*o.i,r*o.i+i*o.r);
    }
    com operator /(const double &o)const{
        return com(r/o,i/o);
    }
}c1[maxn],c2[maxn], omega[maxn];
void read(register int *t,int len)
{
    char s;
    for(register int i=0;i<len;i++)
    {
        s=getchar();
        while(!isdigit(s))s=getchar();
        t[i]=s-'0';
    }
}
void swap(com &x,com &y)
{
    com z=x;
    x=y;
    y=z;
}
void FFT(com *p)
{
    for(register int i=0,j=0; i<nn; i++)
    {
        if(i>j)swap(p[i],p[j]);
        for(register int l=nn>>1; (j^=l)<l; l>>=1);
    }
    for(register int l=2;l<=nn;l<<=1)
    {
        int mid=l/2;
        for(com *t=p;t!=p+nn;t+=l)
        for(register int i=0;i<mid;i++)
        {
            com tp=omega[nn/l*i] * t[i+mid];
            t[i+mid]=t[i]-tp;
            t[i]=t[i]+ tp;
        }
    }
}
com f[maxn],g[maxn];
int suma=0,sumb=0;
char sb[maxn];
int ans[maxn];
int anstot=0;
int main()
{
    scanf("%d%d",&m,&n);
    scanf("%s",sb);
    for(int i=0;i<m;i++)
    {
        if(sb[i]=='*')f[m-1-i].r=0.0,sumb++;
        else f[m-1-i].r=(double)(sb[i]-'a'+1000);
    }
    scanf("%s",sb);
    for(int i=0;i<n;i++)
    {
        if(sb[i]=='*')g[i].r=0.0;
        else g[i].r=1.0/(double)(sb[i]-'a'+1000);   
    } 
    while(nn<m+n-1)nn<<=1;
    for(register int i=0;i<nn;i++)omega[i]=com(std::cos(2.0*pi/(double)nn*(double)(i)),std::sin(2.0*pi/(double)nn*(double)i));
    FFT(f);
    FFT(g);
    for(register int i=0;i<nn;i++)
    {
        omega[i].i*=-1;
        f[i]=f[i]*g[i];
    }
    FFT(f);
    for(register int i=0;i<nn;i++)
        f[i].r/=(double)nn;
    for(int i=m-1;i<n;i++)
    {
        int tmp=(int)(f[i].r+0.5);//四舍五入
        if(abs(f[i].r-(double)tmp)<=1e-6)ans[++anstot]=i-m+2;
    }
    printf("%d\n",anstot);
    for(int i=1;i<=anstot;i++)printf("%d ",ans[i]); 
        return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值