题目大意:先给你一些子串,然后给你一个母串,母串里面的字母可以任意调换位置,问最多这个母串经过一些位置变动最多能包含多少个子串。
分析:可以比较明显的看出来的DP,先求出来ATGC分别有多少,然后再处理,不过有个比较麻烦的地方,因为ATGC的字母数最多是40,因为不知道那种字母所以是40*40*40*40,很明显这种复杂度太高,开始使用了一次打标,把每种状态都保存下来,并且保存它的下一个状态,不过很不幸这种方法TLE了,因为找他的下一个状态不是太容易,看了大神的题解后明白,其实可以使用4种不同的进制来做,进制数就是这个碱基数的总个数,这样复杂度很明显就降低了,而且状态再转移的时候也很容易。
代码如下:
===================================================================================================================================
#include<iostream> #include<algorithm> #include<stdio.h> #include<string.h> #include<queue> using namespace std; const int MAXN = 15007; const int MaxSon = 4; const int oo = 1e9; int Find(char ch) { if(ch == 'A')return 0; if(ch == 'T')return 1; if(ch == 'G')return 2; return 3; } struct Ac_Trie { int next[507][4]; int Fail[507], End[507]; int root, cnt; int newnode() { for(int i=0; i<MaxSon; i++) next[cnt][i] = -1; Fail[cnt] = End[cnt] = false; return cnt++; } void InIt() { cnt = 0; root = newnode(); } void Insert(char s[]) { int now = root; for(int i=0; s[i]; i++) { int k = Find(s[i]); if(next[now][k] == -1) next[now][k] = newnode(); now = next[now][k]; } End[now] += 1 ; } void GetFail() { int now = root; queue<int> Q; for(int i=0; i<MaxSon; i++) { if(next[now][i] == -1) next[now][i] = root; else { Fail[next[now][i]] = root; Q.push(next[now][i]); } } while(Q.size()) { now = Q.front(); Q.pop(); for(int i=0; i<MaxSon; i++) { if(next[now][i] == -1) next[now][i] = next[Fail[now]][i]; else { Fail[next[now][i]] = next[Fail[now]][i]; Q.push(next[now][i]); } } End[now] += End[Fail[now]]; } } }; struct ATGC { int sum[4], bit[4]; int dp[507][MAXN]; Ac_Trie ac; void GetSum(char s[]) { memset(sum, 0, sizeof(sum)); memset(bit, 0, sizeof(bit)); for(int i=0; s[i]; i++) { int k = Find(s[i]); sum[k]++; } } void GetBit() { bit[0] = (sum[1]+1)*(sum[2]+1)*(sum[3]+1); bit[1] = (sum[2]+1)*(sum[3]+1); bit[2] = sum[3]+1; bit[3] = 1; } int GetDp() { memset(dp, -1, sizeof(dp)); dp[0][0] = 0; int ans = 0; for(int A=0; A<=sum[0]; A++) for(int T=0; T<=sum[1]; T++) for(int G=0; G<=sum[2]; G++) for(int C=0; C<=sum[3]; C++) { int k = A*bit[0] + T*bit[1] + G*bit[2] + C*bit[3]; for(int i=0; i<ac.cnt; i++) { if(dp[i][k] == -1)continue; for(int j=0; j<MaxSon; j++) { if(j == 0 && A==sum[0])continue; if(j == 1 && T==sum[1])continue; if(j == 2 && G==sum[2])continue; if(j == 3 && C==sum[3])continue; int next = ac.next[i][j]; int nextk = k + bit[j]; dp[next][nextk] = max(dp[next][nextk], dp[i][k]+ac.End[next]); ans = max(ans, dp[next][nextk]); } } } return ans; } }; ATGC a; int main() { int i, N, t=1; while(scanf("%d", &N), N) { char s[107]; a.ac.InIt(); for(i=0; i<N; i++) { scanf("%s", s); a.ac.Insert(s); } a.ac.GetFail(); scanf("%s", s); a.GetSum(s); a.GetBit(); int ans = a.GetDp(); printf("Case %d: %d ", t++, ans); } return 0; }