P3763 [TJOI2017] DNA

P3763 [TJOI2017] DNA

题目描述

加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列 S,有这个序列的碱基序列就会表现出喜欢吃藕的性状,但是研究人员发现对碱基序列 S,任意修改其中不超过 3 个碱基,依然能够表现出吃藕的性状。现在研究人员想知道这个基因在 DNA 链 S0 上的位置。所以你需要统计在一个表现出吃藕性状的人的 DNA 序列 S0 上,有多少个连续子串可能是该基因,即有多少个 S0 的连续子串修改小于等于三个字母能够变成 S

输入格式a

第一行有一个整数 T,表示有几组数据。

每组数据第一行一个长度不超过 105 的碱基序列 S0

每组数据第二行一个长度不超过 105 的吃藕基因序列 S

输出格式

T 行,第 i 行表示第 i 组数据中,在 S0中有多少个与 S 等长的连续子串可能是表现吃藕性状的碱基序列。

说明/提示

对于 20% 的数据,S0,S 的长度不超过 104

对于 100% 的数据,S0,S 的长度不超过 1050<T10

注:DNA 碱基序列只有 ATCG 四种字符。

Solution:

本来以为字符集大小 4 是突破口,然后发现字符集大小根本不重要。

首先我们思考一下如何匹配两个字符串,kmp 固然可以,但是本题中允许三个位置不相同,这样我们的 kmp 就不太好写了。所以我们考虑使用 hash。

当我们将两个字符串的 hash 求出来之后,check 他们是否相等的时间是 O(1) 的,所以我们就可以枚举 SS0 上出现的位置 sts0 来匹配,那么怎么刻画“允许至多三个位置不相同”呢:

我们发现,匹配长度是满足单调性的,所以我们二分一个最大匹配长度 len 代表 [sts0,sts0+len][sts,sts+len] 是匹配的,且 s0[sts0+len+1]s[sts+len+1] 然后我们跳过 s0[sts0+len+1] , s[sts+len+1] 这两个点,重新以 sts0+len+2 , sts+len+2 为点匹配,这样的失配最多出现 3 次,否则返回 false

然后这题就做完了,时间复杂度 O(nlogn)

Code:

#include<bits/stdc++.h>
#define ull unsigned long long
const int N=1e5+5;
const ull P=19991;
using namespace std;
ull h[2][N],qpow[N];
void init(){qpow[0]=1;for(int i=1;i<N;i++)qpow[i]=qpow[i-1]*P;}
ull Hash(ull h[],int l,int r)
{
return h[r]-h[l-1]*qpow[r-l+1];
}
int match_len(int st_s0,int st_s,int ed_s)
{
int l=1,r=ed_s-st_s+2,res=0;
while(l<=r)
{
int mid=l+r>>1;
if(Hash(h[0],st_s0,st_s0+mid-1)==Hash(h[1],st_s,st_s+mid-1))res=mid,l=mid+1;else r=mid-1;
}
return res;
}
bool check(int st_s0,int ed_s0,int st_s,int ed_s)
{
int len=ed_s-st_s;
for(int i=1;i<=3;i++)
{
int k=match_len(st_s0,st_s,ed_s);
st_s0+=k+1,st_s+=k+1;
if(st_s>ed_s)return 1;
}
return Hash(h[0],st_s0,ed_s0)==Hash(h[1],st_s,ed_s);
}
char s[2][N];
int n,m,ans;
void work()
{
ans=0;
scanf("%s",s[0]+1);n=strlen(s[0]+1);
scanf("%s",s[1]+1);m=strlen(s[1]+1);
for(int i=1;i<=n;i++)h[0][i]=h[0][i-1]*P+s[0][i];
for(int i=1;i<=m;i++)h[1][i]=h[1][i-1]*P+s[1][i];
for(int i=1;i+m-1<=n;i++)if(check(i,i+m-1,1,m))ans++;
printf("%d\n",ans);
}
int main()
{
//freopen("P3763.in","r",stdin);
//freopen("P3763.out","w",stdout);
init();int T;cin>>T;
while(T--)work();
return 0;
}
posted @   liuboom  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示