「TJOI2018」str
碱基序列
题目描述
小豆参加了生物实验室。在实验室里,他主要研究蛋白质。他现在研究的蛋白质是由$k$个氨基酸按一定顺序构成的。每一个氨基酸都可能有$a$种碱基序列$s_{i,j}$构成。
现在小豆有一个碱基串$s$,小豆想知道在这个碱基上都多少中不同的组合方式可能得到这个蛋白质。即求由$k$段字符串有序合并成的字符串$s_1$,有多少种不同方式能够匹配字符串$s$,其中$k$段字符串的选法不同,或者与$s$匹配上的位置不同认为是不同的方式。
输入输出格式
输入格式:第一行一个数,表示这个蛋白质由$k$个氨基酸。
第二行一个字符串$s$,表示小豆现在有的碱基串。
第三行开始接下来$k$行表示第$i$个氨基酸可能的碱基序列,对于第$i$个氨基酸,$a_i$表示这个氨基酸可能的碱基序列种数,接下来$a_i$个字符串表示这$a_i$种可能的碱基序列,用空格隔开。
输出格式:输出一个数目标是不同的方案数(不同的方案数是指不同的子碱基串或者相同的碱基串不同的氨基酸排列方式)
答案对$10^9+7$取模
输入输出样例
说明
样例解释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\%$的数据,$1\leq k\leq25,|s|\leq10000,a_i\leq3$
对于$100\%$的数据,$1\leq k\leq100,|s|\leq10000,a_i\leq10$
题解
参照bestfy的题解。
设f[i][j]表示使用前i个模式串,匹配前j位的贡献。对每个模式串的每种可能转移,使用hash判断是否匹配。
复杂度O(|S|×∑ai)。
#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
rg T data=0,w=1;rg char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-') w=-w;
for(;isdigit(ch);ch=getchar()) data=data*10+ch-'0';
return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
using namespace std;
co int N=1e4+2,P=131,mod=1e9+7;
il int add(int x,int y) {return (x+=y)>=mod?x-mod:x;}
il int mul(int x,int y) {return (ll)x*y%mod;}
int m,n,h[N],p[N];
char s[N];
il int calc(int l,int r){
return add(h[r],mod-mul(h[l-1],p[r-l+1]));
}
int now,f[2][N];
int main(){
read(m);
scanf("%s",s+1),n=strlen(s+1);
p[0]=1;
for(int i=1;i<=n;++i){
p[i]=mul(p[i-1],P);
h[i]=add(mul(h[i-1],P),s[i]);
}
fill(f[0],f[0]+n+1,1);
while(m--){
now^=1,fill(f[now],f[now]+n+1,0); // edit 1:clear from 0
for(int a=read<int>(),l;a--;){
scanf("%s",s+1),l=strlen(s+1);
int hash=0;
for(int i=1;i<=l;++i) hash=add(mul(hash,P),s[i]);
for(int i=1;i<=n-l+1;++i)if(calc(i,i+l-1)==hash)
f[now][i+l-1]=add(f[now][i+l-1],f[now^1][i-1]);
}
}
int ans=0;
for(int i=1;i<=n;++i) ans=add(ans,f[now][i]);
printf("%d\n",ans);
return 0;
}