Luogu P4591 [TJOI2018]碱基序列

[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%的数据,1k25,|s|10000,ai3

对于100%的数据,1k100,|s|10000,ai10

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
typedef unsigned long long ull;
int k, ans, mod = 1000000007;
int cnt[5211314];
ull base[5211314], num[5211314], pre[521][1314];
string seq[521][13], amino;
int dp[521][131400];
//dp[i][j]表示到第i层碱基序列 蛋白质在j位置上对应的最长连接序列
int main() {
scanf("%d", &k);
cin >> amino;
base[0] = 1;
for (int i = 1; i <= amino.size(); ++ i) {
base[i] = base[i - 1] * 131;
//提前预处理出来base
}
for (int i = 1, temp; i <= k; ++ i) {
scanf("%d", &temp);
cnt[i] = temp;
for (int j = 1; j <= cnt[i]; ++ j) {
cin >> seq[i][j];
for (int q = 0; q < seq[i][j].size(); ++ q) {
pre[i][j] = pre[i][j] * base[1] + (ull)seq[i][j][q];
//进行哈希预处理
}
}
}
num[0] = (ull)amino[0];
for (int i = 1; i < amino.size(); ++ i) {
num[i] = num[i - 1] * base[1] + (ull)amino[i];
}
for (int i = 0; i <= amino.size(); ++ i) dp[0][i] = 1;
for (int i = 1; i <= k; ++ i) {
//在第i层
for (int j = 1, len; j <= cnt[i]; ++ j) {
//第j个碱基序列
len = seq[i][j].size();
for (int q = len - 1, l, r; q < amino.size(); ++ q) {
//将当前碱基序列与蛋白质的每一段相同长度的序列进行比较
ull temp;
l = q - len + 1, r = q;
if (l == 0) temp = num[q];
else {
temp = num[r] - num[l - 1] * base[r - l + 1];
}
if (temp == pre[i][j]) {
//若相同则从上一状态转移
dp[i][q + 1] += dp[i - 1][q - len + 1];
dp[i][q + 1] %= mod;
}
}
}
}
for (int i = 0; i < amino.size(); ++ i) {
ans += dp[k][i + 1];
//找对应的值的和
ans %= mod;
}
printf("%d\n", ans);
return 0;
}
posted @   觉清风  阅读(65)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示