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 1≤k≤100, ∣ s ∣ ≤ 10000 |s|\leq10000 ∣s∣≤10000, a i ≤ 10 a_i\leq10 ai≤10。
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(∣s∣∑ai)。
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;
}