题意:考虑只由'A','G','C','T'四种字符组成的DNA字符串。给定一个长度为k的字符串S。计算长度恰好为n且不包含S的字符串的个数。输出答案mod 10009的结果
题解:
对于每一个串s,都可能由一些状态转移到另外一些状态。例如: S=“ATCATCG”,假设我当前s=AATC,而这个状态所对应的状态为*ATC,假如我在这个AATC后加上A,那么s就会转移到*A这个状态。
为什么呢,因为我如果要组成S的话,那么前缀必定相等,所以就会有这样的转移关系!所以就可以用动态规划求解
技巧:先预处理出添加某个字符后转移到的状态表,之后根据一些简单的判断就可以进行DP了
#include <bits/stdc++.h> using namespace std; #define ll long long #define re register #define pb push_back const int mod=10009; void read(int &a) { a=0;int d=1;char ch; while(ch=getchar(),ch>'9'||ch<'0') if(ch=='-') d=-1; a=ch^48; while(ch=getchar(),ch>='0'&&ch<='9') a=(a<<3)+(a<<1)+(ch^48); a*=d; } int n,k; string S; int next[10009][5];///添加某个字符后转移到的状态 int dp[10009][105]; char AGCT[5]={'A','G','C','T'}; void solve() { for(re int i=0;i<k;i++) { for(re int j=0;j<4;j++) { string s=S.substr(0,i)+AGCT[j];///截取S的前i个字符,然后加题目给定的4个字符之一 while(S.substr(0,s.length())!=s)///当S的前缀不是s时做循环 s=s.substr(1);///取s以1之后的所有字符,相当于删除第一个字符后剩下的所有字符 next[i][j]=s.length();///添加j字符后转移到的状态的字符前缀长度 } } } int main() { read(n),read(k);cin>>S; solve(); dp[0][0]=1; for(re int i=1;i<k;i++) dp[0][i]=0; for(re int t=0;t<n;t++) { for(re int i=0;i<k;i++) dp[t+1][i]=0; for(re int i=0;i<k;i++) { for(re int j=0;j<4;j++) { int ti=next[i][j]; if(ti==k) continue;///如果当前字符前缀长度已经为k,那么明显是不符合要求的 //cout<<ti<<" "<<i<<endl; dp[t+1][ti]=(dp[t+1][ti]+dp[t][i])%mod;///t+1长度的,只要当前next[i][j]匹配长度不为k,那么ti可以由i转移过来,所以就有这个转移式!!! } } } int ans=0; for(re int i=0;i<k;i++) ans=(ans+dp[n][i])%mod;///将长度为n的,与S匹配最长为i的串全部累加就是答案 printf("%d\n",ans); }