「TJOI 2018」碱基序列

传送门


problem

小豆参加了生物实验。在实验室里,他主要研究蛋白质。他现在研究的蛋白质是由 k k k 个氨基酸按一定顺序构成的。每一个氨基酸都可能有 a a a 种碱基序列 s i , j s_{i,j} si,j 构成。

现在小豆有一个碱基串 s s s,小豆想知道在这个碱基上都多少中不同的组合方式可能得到这个蛋白质。即求由 k k k 段字符串有序合并成的字符串 s 1 s_1 s1,有多少种不同方式能够匹配字符串 s s s,其中 k k k 段字符串的选法不同,或者与 s s s 匹配上的位置不同认为是不同的方式。

数据范围: 1 ≤ k ≤ 100 1\leq k\leq100 1k100 ∣ s ∣ ≤ 10000 |s|\leq10000 s10000 a i ≤ 10 a_i\leq10 ai10

PS:这题的题描有点模糊,可以参考一下样例及样例解释。


solution

这道题其实挺简单的。

f i , j f_{i,j} fi,j 表示前 i i i 个串组成的字符串,与字符串 s s s 的前 j j j 个字符匹配的方案。

转移很简单,用 hash 判一下字符串匹配,然后暴力转移就可以了。代码也很好写。

时间复杂度 O ( ∣ s ∣ ∑ a i ) O(|s|\sum a_i) O(sai)


code

#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
const int N=1e4+5,P=1e9+7;
int n,k,f[105][N];
ull b=131,Pow[N],Hash[N];
void Add(int &x,int y)  {x=(x+y>=P)?x+y-P:x+y;}
char S[N],T[N];
void prework(){
	Pow[0]=1;
	for(int i=1;i<=n;++i)  Pow[i]=Pow[i-1]*b;
	for(int i=1;i<=n;++i)  Hash[i]=Hash[i-1]*b+S[i]-'A'+1;
}
ull Get(int l,int r){
	return Hash[r]-Hash[l-1]*Pow[r-l+1];
}
int main(){
	scanf("%d%s",&k,S+1);
	n=strlen(S+1),prework();
	for(int i=0;i<=n;++i)  f[0][i]=1;
	for(int i=1,num,L;i<=k;++i){
		scanf("%d",&num);
		while(num--){
			ull now=0;
			scanf("%s",T+1),L=strlen(T+1);
			for(int j=1;j<=L;++j)  now=now*b+T[j]-'A'+1;
			for(int j=L;j<=n;++j)
				if(Get(j-L+1,j)==now)  Add(f[i][j],f[i-1][j-L]);
		}
	}
	int ans=0;
	for(int i=1;i<=n;++i)  Add(ans,f[k][i]);
	printf("%d\n",ans);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值