返回顶部

碱基序列(str)

[TJOI2018] 碱基序列

题目描述

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

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

输入格式

第一行一个数,表示这个蛋白质由 k 个氨基酸组成。

第二行一个字符串 s,表示小豆现在有的碱基串。

第三行开始接下来 k 行表示第 i 个氨基酸可能的碱基序列,对于第 i 个氨基酸,ai 表示这个氨基酸可能的碱基序列种数,接下来 ai 个字符串表示这 ai 种可能的碱基序列,用空格隔开。

输出格式

输出一个数目标是不同的方案数(不同的方案数是指不同的子碱基串或者相同的碱基串不同的氨基酸排列方式)

答案对 109+7 取模。

样例 #1

样例输入 #1

2
ABC
2 A AB
2 C BC

样例输出 #1

2

样例 #2

样例输入 #2

2
AAA
2 A AA
2 A AA

样例输出 #2

4

提示

样例 1 解释

  • 第一个选 A 第二个选 C,得到 AC 能够与 ABC 产生 0 种匹配方式;
  • 第一个选 A 第二个选 BC,得到 ABC 能够与 ABC 产生 1 种匹配方式;
  • 第一个选 AB 第二个选 C,得到 ABC 能够与 ABC 产生 1 种匹配方式;
  • 第一个选 AB 第二个选 BC,得到 ABBC 能够与 ABC 产生 0 种匹配方式。

所以一共 2 种。

样例 2 解释

  • 第一个选 A 第二个选 A,得到 AA 能够与 AAA 产生 2 种匹配方式;
  • 第一个选 A 第二个选 AA,得到 AAA 能够与 AAA 产生 1 种匹配方式;
  • 第一个选 AA 第二个选 A,得到 AAA 能够与 AAA 产生 1 种匹配方式;
  • 第一个选 AA 第二个选 AA,得到 AAAA 能够与 AAA 产生 0 种匹配方式。

所以一共 4 种。

数据范围及约定

  • 对于 30% 的数据,1k251|s|100001ai3
  • 对于 100% 的数据,1k1001|s|100001ai10。碱基序列的长度均不超过 15

这题可以用hash和DP
DP类似背包DP,类似分组背包,设一个长度为i的序列j个碱基,
这么转移f[i][j]+=f[ilenj,k+1][j1](hash(s1)==hash(sj,k))
用表示第i个氨基酸选完后,完成前j个碱基有几种可能性(由于没有说从必须把他完全填满,非常坑,所以需要赋初值dp[i][0]为1,最后把1ldp[i][n]

点击查看代码
#include <bits/stdc++.h>
#define ll unsigned long long
using namespace std;
const int mod =1e9+7;
const int B = 233 , N =10005;
int n,m;
ll h[N],p[N],ha[N][N],let[N][N];int num[N];
char s1[N];
char a[115][115][N];
void init()
{
	p[0]=1;
	for(int i=1;i<=N-5;i++)
	{
		p[i]=B*p[i-1];
	}
	for(int i=1;i<=strlen(s1+1);i++)
	{
		h[i]=(h[i-1]*B+s1[i]);
	}
}
ll get_hash(int x,int y,int len)
{
	ll res=0;
	for(int i=1;i<=len;i++)
	{
		res=(res*B+a[x][y][i]);
	}
	return res;
}
ll ask(int l,int r)
{
	return (h[r]-h[l-1]*p[r-l+1]);
}
int f[N][115];
main()
{
	scanf("%d",&n);
	cin>>s1+1;
	init();
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&num[i]);
		for(int j=1;j<=num[i];j++)
		{
			cin>>a[i][j]+1;
//			a[i][j][0]=' ';
			let[i][j]=strlen(a[i][j]+1);
			ha[i][j]=get_hash(i,j,let[i][j]);
		}	
	}
	int len=strlen(s1+1);
	for(int i=0;i<=len;i++)f[i][0]=1;
	for(int i=1;i<=len;i++)
	{
		for(int j=1;j<=n;j++)
		{
			for(int k=1;k<=num[j];k++)
			{
				if(i<let[j][k])continue;
//				cout<<i<<" "<<j<<" "<<k<<" "<<ha[j][k]<<" "<<ask(i-let[j][k]+1,i)<<endl;
				if(ha[j][k]==ask(i-let[j][k]+1,i))
				{
					f[i][j]=(f[i-let[j][k]][j-1]+f[i][j])%mod;
//					cout<<i<<" "<<j<<" "<<f[i][j]<<endl;
				}
			}
		}		
	}
	ll ans=0;
	for(int i=1;i<=len;i++)
	{
		ans=(ans+f[i][n])%mod;
	}
	cout<<ans;
	return 0;
}
posted @   wlesq  阅读(91)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示