[BZOJ4259]残缺的字符串

题目大意

给定模板串 A 和母串B,长度分别为 m n,你需要统计模板串在母串中所有出现的位置。
字符集都是小写拉丁字母,而且存在通配符 能够匹配任意字符。

1m,n3×105


题目分析

考虑将 A 串倒置,然后令

ai=0,ord(Ai)ord(a)+1,Ai is otherwise

B 串同理,那么我们令
Fi=j=0i(aibi)2aibi

如果 i 为结尾能够匹配模板串,就有Fi=0,将上式拆开可以变成一个卷积形式,使用FFT就好了。
时间复杂度 O(nlogn)


代码实现

为什么我的DFT跑起来的时间是别人的两倍。。。
于是BZOJ上T成狗。。。
有没有大神来指明一下哪里常数大了???

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cmath>

#define sqr(x) (x*x)
#define cube(x) (x*x*x)

using namespace std;

typedef double db;

int buf[30];

void write(int x)
{
    if (x<0) putchar('-'),x=-x;
    for (;x;x/=10) buf[++buf[0]]=x%10;
    if (!buf[0]) buf[++buf[0]]=0;
    for (;buf[0];putchar('0'+buf[buf[0]--]));
}

const db pi=acos(-1);
const int N=524300;

struct Z
{
    db x,y;

    inline Z(){}  
    inline Z(db x_,db y_):x(x_),y(y_){};  
    inline Z operator+(Z const p){return Z(x+p.x,y+p.y);}
    inline Z operator-(Z const p){return Z(x-p.x,y-p.y);}
    inline Z operator*(Z const p){return Z(x*p.x-y*p.y,x*p.y+y*p.x);}
    inline Z operator/(db const k){return Z(x/k,y/k);}
}A[N],B[N],C[N],t[N],omega[N];

db S_[N],T_[N],res[N];
int trs[N],mat[N];
char S[N],T[N];
int n,m,L,cnt;

void DFT(Z *a,int len,int sig)
{
    for (int i=0;i<len;++i) t[trs[i]]=a[i];
    Z w;
    for (int l=2,half,p;l<=len;l<<=1)
    {
        half=l>>1,p=len/l;
        for (int i=0;i<half;++i)
        {
            w=omega[sig>0?p*i:len-p*i];
            for (int j=i;j<len;j+=l)
            {
                Z u=t[j],v=w*t[j+half];
                t[j]=u+v,t[j+half]=u-v;
            }
        }
    }
    for (int i=0;i<len;++i) a[i]=t[i];
}

void pre()
{
    for (L=1;L<n||L<m;L<<=1);
    for (int i=0;i<L;++i)
    {
        int ret=0;
        for (int j=1,x=i;j<L;j<<=1,x>>=1) ret=ret<<1|(x&1);
        trs[i]=ret;
    }
    for (int i=0;i<=L;++i) omega[i]=Z(cos(2.*pi*i/L),sin(2.*pi*i/L));
}

void calc()
{
    for (int i=0;i<n;++i) S_[i]=S[i]=='*'?0:S[i]-'a'+1;
    for (int i=0;i<m;++i) T_[i]=T[i]=='*'?0:T[i]-'a'+1;
    for (int i=0;i<L;++i) A[i]=Z(cube(S_[i]),0);
    for (int i=0;i<L;++i) B[i]=Z(T_[i],0);
    DFT(A,L,1),DFT(B,L,1);
    for (int i=0;i<L;++i) C[i]=A[i]*B[i];
    DFT(C,L,-1);
    for (int i=0;i<n;++i) C[i]=C[i]/L;
    for (int i=0;i<n;++i) res[i]=round(C[i].x);
    for (int i=0;i<L;++i) A[i]=Z(sqr(S_[i]),0);
    for (int i=0;i<L;++i) B[i]=Z(sqr(T_[i]),0);
    DFT(A,L,1),DFT(B,L,1);
    for (int i=0;i<L;++i) C[i]=A[i]*B[i];
    DFT(C,L,-1);
    for (int i=0;i<n;++i) C[i]=C[i]/L;
    for (int i=0;i<n;++i) res[i]-=2.*round(C[i].x);
    for (int i=0;i<L;++i) A[i]=Z(S_[i],0);
    for (int i=0;i<L;++i) B[i]=Z(cube(T_[i]),0);
    DFT(A,L,1),DFT(B,L,1);
    for (int i=0;i<L;++i) C[i]=A[i]*B[i];
    DFT(C,L,-1);
    for (int i=0;i<n;++i) C[i]=C[i]/L;
    for (int i=0;i<n;++i) res[i]+=round(C[i].x);
}

int main()
{
    freopen("string.in","r",stdin),freopen("string.out","w",stdout);
    scanf("%d%d",&m,&n);
    scanf("%s",T),scanf("%s",S),reverse(T,T+m),pre();
    calc();
    for (int i=m-1;i<n;++i)
        if (fabs(res[i])<=1e-8) mat[++cnt]=i+2-m;
    write(cnt),putchar('\n');
    for (int i=1;i<=cnt;++i) write(mat[i]),putchar(i<cnt?' ':'\n');
    fclose(stdin),fclose(stdout);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值