LG3763 [TJOI2017]DNA
作者:互联网
DNA
题目描述
加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列S,有这个序列的碱基序列就会表现出喜欢吃藕的性状,但是研究人员发现对碱基序列S,任意修改其中不超过3个碱基,依然能够表现出吃藕的性状。现在研究人员想知道这个基因在DNA链S0上的位置。所以你需要统计在一个表现出吃藕性状的人的DNA序列S0上,有多少个连续子串可能是该基因,即有多少个S0的连续子串修改小于等于三个字母能够变成S。
输入输出格式
输入格式:第一行有一个数T,表示有几组数据 每组数据第一行一个长度不超过10^5的碱基序列S0
每组数据第二行一个长度不超过10^5的吃藕基因序列S
输出格式:共T行,第i行表示第i组数据中,在S0中有多少个与S等长的连续子串可能是表现吃藕性状的碱基序列
输入输出样例
输入样例#1: 复制1 ATCGCCCTA CTTCA输出样例#1: 复制
2
说明
对于20%的数据,S0,S的长度不超过10^4
对于20%的数据,S0,S的长度不超过10^5,0<T<=10
分析
随便翻的时候看到了这道题。hash好题,枚举每个位置判断一下能否在三次以内匹配就行了。
时间复杂度\(O(n\log m)\)
代码
#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
rg T data=0;rg char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-') data=-data;
for(;isdigit(ch);ch=getchar()) data=data*10+ch-'0';
return data;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef unsigned long long ull;
co int N=1e5+1,D=131;
char a[N],b[N];
int n,m;
ull p[N],f[N],g[N];
ull ask(ull*f,int l,int r){
return f[r]-f[l-1]*p[r-l+1];
}
int lcp(int x,int y,int r){
int l=0;
while(l<r){
int mid=l+r+1>>1;
if(ask(f,x,x+mid-1)==ask(g,y,y+mid-1)) l=mid;
else r=mid-1;
}
return l;
}
bool check(int x){
int r=x+m-1,y=1;
for(int i=0;i<3;++i){
int t=lcp(x,y,m-y+1);
x+=t+1,y+=t+1;
if(y>m) return 1;
}
return ask(f,x,r)==ask(g,y,m);
}
void DNA(){
scanf("%s",a+1),n=strlen(a+1);
scanf("%s",b+1),m=strlen(b+1);
if(n<m) return puts("0"),void();
for(int i=1;i<=n;++i) f[i]=f[i-1]*D+a[i];
for(int i=1;i<=m;++i) g[i]=g[i-1]*D+b[i];
int ans=0;
for(int i=1;i+m-1<=n;++i)if(check(i)) ++ans;
printf("%d\n",ans);
}
int main(){
p[0]=1;for(int i=1;i<N;++i) p[i]=p[i-1]*D;
for(int t=read<int>();t--;) DNA();
return 0;
}
标签:LG3763,ch,DNA,int,S0,TJOI2017,序列,return,data 来源: https://www.cnblogs.com/autoint/p/10848414.html