题目大意:
现在给出 N ( N <= 50)个DNA序列(长度都不超过10)表示数论基因,现在给一个长度不超过40的DNA序列,要求将这个序列重新排列使得包行的数论基因数量最多,输出最多能包含多少次数论基因
其中顺论基因每出现一次就要算一次,多个基因序列重叠出现也要算
大致思路:
首先容易想到将N个DNA序列加入Trie树建立AC自动机的状态转移图m,然后 dp[i][x1][x2][x3][x4]表示到达节点 i 处已经使用了 x1个A,x2个G,x3个C, x4个T能得到的最大序列数,这样很容易找到状态转移方程
但是受到内存的限制,不能开41*41*41*41的数组, 考虑到x1 + x2 + x3 + x4 <= 40 并且 x1 <= 40 , x2 <= 40, x3 <= 40, x4 <= 40可以发现对于固定的x1,x2,x3,x4的上限,存在的状态数不会超过11*11*11*11,这样如果用变进制来表示这个状态然后转为唯一对应的十进制数表示,就只需要一个11*11*11*11的数组
这里采用的变进制见代码
另外做的时候一直被卡时间,后来到HDU的discuss上看见这样一段话:
对于 dp[x][y];
for(int i = 0; i < x; i++)
for(int j = 0; j < y; j++)
dp[i][j] = 1;
比
for(int i = 0; i < x; i++)
for(int j = 0; j < y; j++)
dp[j][i] = 1;
耗时要少
之前都没听说过不过这枚改了一下dp数组一二维的位置之后时间瞬间降到了2000ms附近...
代码如下:
Result : Accepted Memory : 29612 KB Time : 2390 ms
/* * Author: Gatevin * Created Time: 2014/11/22 19:49:34 * File Name: Kagome.cpp */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; int n; map <char, int> M; int dp[11*11*11*11 + 1][510];//我写成dp[510][11*11*11*11 + 1]的版本就TLE了... struct Trie { int next[510][4], fail[510], end[510]; int L, root; int newnode() { for(int i = 0; i < 4; i++) next[L][i] = -1; end[L++] = 0; return L - 1; } void init() { L = 0; root = newnode(); return; } void insert(char *s) { int now = root; for(; *s; s++) { if(next[now][M[*s]] == -1) next[now][M[*s]] = newnode(); now = next[now][M[*s]]; } end[now]++; return; } void build() { queue <int> Q; fail[root] = root; Q.push(root); while(!Q.empty()) { int now = Q.front(); Q.pop(); end[now] += end[fail[now]]; for(int i = 0; i < 4; i++) if(next[now][i] == -1) next[now][i] = now == root ? root : next[fail[now]][i]; else { fail[next[now][i]] = now == root ? root : next[fail[now]][i]; Q.push(next[now][i]); } } return; } int solve(char *s) { int bit[4]; int num[4]; memset(num, 0, sizeof(num)); for(; *s; s++) num[M[*s]]++; bit[0] = 1; bit[1] = (num[0] + 1); bit[2] = (num[0] + 1)*(num[1] + 1); bit[3] = (num[0] + 1)*(num[1] + 1)*(num[2] + 1); int status = bit[0]*num[0] + bit[1]*num[1] + bit[2]*num[2] + bit[3]*num[3]; memset(dp, -1, sizeof(dp)); dp[0][0] = 0; for(int A = 0; A <= num[0]; A++) for(int G = 0; G <= num[1]; G++) for(int C = 0; C <= num[2]; C++) for(int T = 0; T <= num[3]; T++) { int now = A*bit[0] + G*bit[1] + C*bit[2] + T*bit[3]; for(int i = 0; i < L; i++) { if(dp[now][i] == -1) continue; for(int k = 0; k < 4; k++) { if(k == 0 && A == num[0]) continue; if(k == 1 && G == num[1]) continue; if(k == 2 && C == num[2]) continue; if(k == 3 && T == num[3]) continue; dp[now + bit[k]][next[i][k]] = max(dp[now][i] + end[next[i][k]], dp[now + bit[k]][next[i][k]]); } } } int ans = 0; for(int i = 0; i < L; i++) ans = max(ans, dp[status][i]); return ans; } }; Trie AC; int main() { char ts[41]; int cas = 0; M['A'] = 0; M['G'] = 1; M['C'] = 2; M['T'] = 3; while(scanf("%d", &n) && n) { cas++; AC.init(); while(n--) { scanf("%s", ts); AC.insert(ts); } scanf("%s", ts); AC.build(); printf("Case %d: %d\n", cas, AC.solve(ts)); } return 0; }