【BZOJ4892】DNA(后缀数组)

题面

BZOJ

洛谷

题解

看到这道题目,我第一反应是\(FFT\)???

然后大力码出了一个\(FFT\)

就像这样

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 200000
inline int read()
{
RG int x=0,t=1;RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
const double Pi=acos(-1);
struct Complex{double a,b;}A[MAX],B[MAX],W[MAX];
Complex operator+(Complex a,Complex b){return (Complex){a.a+b.a,a.b+b.b};}
Complex operator-(Complex a,Complex b){return (Complex){a.a-b.a,a.b-b.b};}
Complex operator*(Complex a,Complex b){return (Complex){a.a*b.a-a.b*b.b,a.b*b.a+a.a*b.b};}
char C[MAX],S[MAX],dna[4]={'A','G','C','T'};
int n,m,ans[MAX],N,r[MAX],l;
void FFT(Complex *P,int opt)
{
for(int i=1;i<N;++i)if(i<r[i])swap(P[i],P[r[i]]);
for(int i=1;i<N;i<<=1)
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
{
Complex w=(Complex){W[N/i*k].a,W[N/i*k].b*opt};
Complex X=P[j+k],Y=w*P[i+j+k];
P[j+k]=X+Y;P[i+j+k]=X-Y;
}
if(opt==-1)for(int i=0;i<N;++i)P[i].a/=N;
}
int main()
{
int T=read();
while(T--)
{
scanf("%s",C);scanf("%s",S);l=0;
n=strlen(C);m=strlen(S);reverse(&S[0],&S[m]);
for(N=1;N<n+m;N<<=1)++l;
for(int i=0;i<n;++i)ans[i]=0;
for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
for(int i=1;i<N;i<<=1)
for(int k=0;k<i;++k)W[N/i*k]=(Complex){cos(k*Pi/i),sin(k*Pi/i)};
for(int p=0;p<4;++p)
{
for(int i=0;i<N;++i)A[i]=B[i]=(Complex){0,0};
for(int i=0;i<n;++i)A[i]=(Complex){C[i]==dna[p]?1.0:0,0};
for(int i=0;i<m;++i)B[i]=(Complex){S[i]==dna[p]?1.0:0,0};
FFT(A,1);FFT(B,1);
for(int i=0;i<N;++i)A[i]=A[i]*B[i];
FFT(A,-1);
for(int i=m-1;i<n;++i)ans[i]+=(int)(A[i].a+0.5);
}
int tot=0;
for(int i=m-1;i<n;++i)if(ans[i]+3>=m)++tot;
printf("%d\n",tot);
}
return 0;
}

然后洛谷上交一发

恩。。。\(FFT\)果然常数名不虚传

那么就用\(NTT\)吧

就像这样

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 200000
inline int read()
{
RG int x=0,t=1;RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
const int MOD=998244353;
int A[MAX],B[MAX],W[MAX];
char C[MAX],S[MAX],dna[4]={'A','G','C','T'};
int n,m,ans[MAX],N,r[MAX],l;
int fpow(int a,int b)
{
int s=1;
while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}
return s;
}
void NTT(int *P,int opt)
{
for(int i=1;i<N;++i)if(i>r[i])swap(P[i],P[r[i]]);
for(int i=1;i<N;i<<=1)
{
int w=fpow(3,(MOD-1)/(i<<1));W[0]=1;
for(int j=1;j<i;++j)W[j]=1ll*W[j-1]*w%MOD;
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
{
int X=P[j+k],Y=P[i+j+k]*1ll*W[k]%MOD;
P[j+k]=(X+Y)%MOD;P[i+j+k]=(X+MOD-Y)%MOD;
}
}
if(opt==-1)
{
reverse(&P[1],&P[N]);
for(int i=0,inv=fpow(N,MOD-2);i<N;++i)P[i]=1ll*P[i]*inv%MOD;
}
}
int main()
{
int T=read();
while(T--)
{
scanf("%s",C);scanf("%s",S);l=0;
n=strlen(C);m=strlen(S);reverse(&S[0],&S[m]);
for(N=1;N<n+m;N<<=1)++l;
for(int i=0;i<n;++i)ans[i]=0;
for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
for(int p=0;p<4;++p)
{
for(int i=0;i<N;++i)A[i]=B[i]=0;
for(int i=0;i<n;++i)A[i]=C[i]==dna[p];
for(int i=0;i<m;++i)B[i]=S[i]==dna[p];
NTT(A,1);NTT(B,1);
for(int i=0;i<N;++i)A[i]=1ll*A[i]*B[i]%MOD;;
NTT(A,-1);
for(int i=m-1;i<n;++i)ans[i]+=A[i];
}
int tot=0;
for(int i=m-1;i<n;++i)if(ans[i]+3>=m)++tot;
printf("%d\n",tot);
}
return 0;
}

这样就很开心了

当然,这个时间在洛谷能够排到多少呢?

倒数第一诶。。。

很舒服啊。

然后就在\(BZOJ\)上交了一发,然后\(TLE\)了。

问了一下早就切掉了这道题的\(zsy\)。

原来这题根本就不是\(NNT\)啊。。。


我们考虑如何暴力,

求出\(SA\)之后我们可以做到\(O(1)\)查询\(lcp\)

然后暴力往后跳就行了,因为不会跳超过三次,

所以直接暴力就行了。。。

时间复杂度\(O(Tnlogn)\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 222222
inline int read()
{
RG int x=0,t=1;RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
int n,m,N;
int SA[MAX],rk[MAX],hg[20][MAX],lg[MAX],a[MAX],t[MAX],x[MAX],y[MAX];
bool cmp(int i,int j,int k){return y[i]==y[j]&&y[i+k]==y[j+k];}
void GetSA()
{
int m=50;
for(int i=1;i<=m;++i)t[i]=0;
for(int i=1;i<=n;++i)t[x[i]=a[i]]++;
for(int i=1;i<=m;++i)t[i]+=t[i-1];
for(int i=n;i>=1;--i)SA[t[x[i]]--]=i;
for(int k=1;k<=n;k<<=1)
{
int p=0;
for(int i=1;i<=n;++i)y[i]=0;
for(int i=n-k+1;i<=n;++i)y[++p]=i;
for(int i=1;i<=n;++i)if(SA[i]>k)y[++p]=SA[i]-k;
for(int i=0;i<=m;++i)t[i]=0;
for(int i=1;i<=n;++i)t[x[y[i]]]++;
for(int i=1;i<=m;++i)t[i]+=t[i-1];
for(int i=n;i>=1;--i)SA[t[x[y[i]]]--]=y[i];
swap(x,y);
x[SA[1]]=p=1;
for(int i=2;i<=n;++i)x[SA[i]]=cmp(SA[i],SA[i-1],k)?p:++p;
if(p>=n)break;
m=p;
}
for(int i=1;i<=n;++i)rk[SA[i]]=i;
for(int i=1,j=0;i<=n;++i)
{
if(j)--j;
while(a[i+j]==a[SA[rk[i]-1]+j])++j;
hg[0][rk[i]]=j;
}
for(int j=1;j<=lg[n];++j)
for(int i=1;i+(1<<j)-1<=n;++i)
hg[j][i]=min(hg[j-1][i],hg[j-1][i+(1<<(j-1))]);
}
int lcp(int i,int j)
{
i=rk[i];j=rk[j];if(i>j)swap(i,j);
if(i==j)return 1e9;++i;
int l=lg[j-i+1];
return min(hg[l][i],hg[l][j-(1<<l)+1]);
}
char S0[MAX],S[MAX];
int main()
{
int T=read();
for(int i=2;i<MAX;++i)lg[i]=lg[i>>1]+1;
while(T--)
{
scanf("%s",S0+1);scanf("%s",S+1);
n=strlen(S0+1);m=strlen(S+1);
int N=n;
for(int i=1;i<=n;++i)a[i]=S0[i]-64;
a[++n]=27;
for(int i=1;i<=m;++i)a[++n]=S[i]-64;
GetSA();int ans=0;
for(int i=1;i<=N-m+1;++i)
{
int tt=0;
for(int j=1;j<=m&&tt<=3;)
{
if(a[i+j-1]!=a[N+1+j])++tt,++j;
else j+=lcp(i+j-1,N+1+j);
}
ans+=tt<=3;
}
printf("%d\n",ans);
}
return 0;
}

【BZOJ4892】DNA(后缀数组)的更多相关文章

  1. [BZOJ4892][TJOI2017]DNA(后缀数组)

    题目描述 加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列S,有这个序列的碱基序列就会表现出喜欢吃藕的性状,但是研究人员发现对碱基序列S,任意修改其中不超过3个碱基,依然能够表现出吃藕的性状 ...

  2. [TJOI2017]DNA --- 后缀数组

    [TJOI2017]DNA 题目描述 加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列S, 有这个序列的碱基序列就会表现出喜欢吃藕的性状,但是研究人员发现对碱基序列S,任意修改其中不超过3个 ...

  3. [TJOI2017] DNA - 后缀数组,稀疏表

    [TJOI2017] DNA Description 求模式串与主串的匹配次数,容错不超过三个字符. Solution 枚举每个开始位置,进行暴力匹配,直到失配次数用光或者匹配成功.考虑到容错量很小, ...

  4. 洛谷P3763 [TJOI2017]DNA(后缀数组 RMQ)

    题意 题目链接 Sol 这题打死我也不会想到后缀数组的,应该会全程想AC自动机之类的吧 但知道这题能用后缀数组做之后应该就不是那么难了 首先把\(S\)和\(S0\)拼到一起跑,求出Height数组 ...

  5. 洛谷P3763 [Tjoi2017]DNA 【后缀数组】

    题目链接 洛谷P3763 题解 后缀数组裸题 在BZOJ被卡常到哭QAQ #include<algorithm> #include<iostream> #include< ...

  6. 字符串 --- KMP Eentend-Kmp 自动机 trie图 trie树 后缀树 后缀数组

    涉及到字符串的问题,无外乎这样一些算法和数据结构:自动机 KMP算法 Extend-KMP 后缀树 后缀数组 trie树 trie图及其应用.当然这些都是比较高级的数据结构和算法,而这里面最常用和最熟 ...

  7. poj 3294 后缀数组 多字符串中不小于 k 个字符串中的最长子串

    Life Forms Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 16223   Accepted: 4763 Descr ...

  8. Blue Jeans - poj 3080(后缀数组)

    大致题意: 给出n个长度为60的DNA基因(A腺嘌呤 G鸟嘌呤 T胸腺嘧啶 C胞嘧啶)序列,求出他们的最长公共子序列 使用后缀数组解决 #include<stdio.h> #include ...

  9. uva11107(后缀数组)

    uva11107 题意 输入 n 个 DNA 序列,求出长度最大的字符串,使得它在超过一半的 DNA 序列中连续出现.如果有多解,按字典序输出. 分析 论文 后缀数组经典题.加深几个关键数组的印象. ...

随机推荐

  1. RSA算法笔记+理解

    明天网络安全考试了,看了一下午,还没理解透,持续更新... 质数: 除了1和它本身以外不再有其他因素的数互质关系: 两个正整数,除了1以外,没有其他公因子RSA实现了非对称加密DES实现了对称加密** ...

  2. 记一次Log4j2日志无法输出的 心酸史

    问题描述:部分日志无法输出到日志文件中. 项目中的代码: @Resource private ConfigInfo configInfo; private static final Logger lo ...

  3. Python基础 之 文件操作

    文件操作 一.路径 文件绝对路径:d:\python.txt 文件相对路径:在IDEA左边的文件夹中 二.编码方式 utf-8 gbk... 三.操作方式 1.只读 r 和 rb 绝对路径的打开操作 ...

  4. 校招小白机考入坑之从键盘输入java的各种数据类型

    //1.从键盘输入一个整型(其他基本类型类似) Scanner sc =new Scanner(System.in); sc.hasNextInt(); int str1 = sc.nextInt() ...

  5. Balanced Lineup:线段树:区间最值 / RMQ

    不要被线段树这个名字和其长长的代码吓到. D - Balanced Lineup Description For the daily milking, Farmer John's N cows (1 ...

  6. 阿里云搭建bind服务,外网ip不能用来解析问题解决

    options { listen-on port 53 { any; }; //端口开放any listen-on-v6 port 53 { ::1; }; directory "/var/ ...

  7. cookie,localstorge,sessionstorge三者总结

    相同点:都是客户端存储东西的: 不同: 1大小,cookie最小;locastorge最大 2 cookie设置好会在header头里面自动带的:但是ls和ss不会:ls同个浏览下不同网页(非跨域)都 ...

  8. return阻止js继续向下执行

    终止JS运行有如下几种可能: 终止函数的运行的方式有两种 在函数中使用return,则当遇到return时,函数终止执行,控制权继续向下运行 在函数中使用try-catch异常处理,需要结束时,使用t ...

  9. lintcode-11-二叉查找树中搜索区间

    二叉查找树中搜索区间 给定两个值 k1 和 k2(k1 < k2)和一个二叉查找树的根节点.找到树中所有值在 k1 到 k2 范围内的节点.即打印所有x (k1 <= x <= k2 ...

  10. LintCode-381.螺旋矩阵 II

    螺旋矩阵 II 给你一个数n生成一个包含1-n^2的螺旋形矩阵 样例 n = 3 矩阵为 [     [ 1, 2, 3 ],     [ 8, 9, 4 ],     [ 7, 6, 5 ] ] 标 ...