这题目是去年大连regional的题目,当时比赛的时候已经看出是道自动机+dp的题目了,但是无奈当时自己的自动机基础太弱了,被虐了一顿。今天再来做这道题目,感觉还好,思路还是蛮清晰的,但是却看出了长时间写代码少代码能力的不足。。。一个<=写成了<然后昨天WA了一晚上。。。
这题和传统的自动机+dp差不多,主要区别在于每个带权基因串只计算一次,需要用状态压缩(字符串个数最大只有10个)。dp[l][n][state]表示长度为l的字符串,后缀为自动机中第n类状态(自动机中每个节点代表一类状态),是否可取道带权基因串组合状态为state的情况;如果可以dp[l][n][state]为true,否则为false。计算的时候和大部分自动机dp一样,暴力递推求解就可以了。
#include <iostream> #include <cstdio> #include <cstring> #include <vector> #include <map> #include <string> #include <utility> #include <algorithm> using namespace std; const int N = 10; struct node { int num, index; node *pre, *next[4]; void init() { num = index = 0; pre = 0; memset(next, 0, sizeof(next)); } }; int n, l, cnt, val[10], sval[1<<N]; bool dp[2][N*150][1<<N]; char str[150]; map <char, int> h; node trie[N*150]; node *root, *queue[N*150]; void insertStr(node *root, char *str, int pos) { int lab, len = strlen(str); for(int i = 0; i < len; i++) { lab = h[str[i]]; if(root->next[lab] == NULL) { root->next[lab] = &trie[++cnt]; root->next[lab]->init(); root->next[lab]->index = cnt; } root = root->next[lab]; } root->num |= (1<<pos); } void buildAC() { int head, tail; node *u; root->pre = root; head = tail = 0; queue[0] = root; while(head <= tail) { u = queue[head++]; u->num |= u->pre->num; for(int i = 0; i < 4; i++) { if(u->next[i] == 0) { if(u == root) u->next[i] = root; else u->next[i] = u->pre->next[i]; } else { if(u == root) u->next[i]->pre = root; else u->next[i]->pre = u->pre->next[i]; queue[++tail] = u->next[i]; } } } } int main() { //freopen("data.in", "r", stdin); h['A'] = 0; h['G'] = 1; h['T'] = 2; h['C'] = 3; while(scanf("%d%d", &n, &l) != EOF) { memset(trie, 0, sizeof(trie)); root = &trie[0]; root->init(); cnt = 0; for(int i = 0; i < n; i++) { scanf(" %s%d", str, &val[i]); insertStr(root, str, i); } memset(sval, 0, sizeof(sval)); for(int state = 0; state < (1<<n); state++) { for(int j = 0; j < n; j++) if(state&(1<<j)) sval[state] += val[j]; } buildAC(); memset(dp, false, sizeof(dp)); int u, v; u = 0; dp[u][0][0] = true; for(int i = 0; i < l; i++) { v = u^1; memset(dp[v], false, sizeof(dp[v])); for(int j = 0; j <= cnt; j++) for(int k = 0; k < (1<<n); k++) { if(!dp[u][j][k]) continue; else { for(int p = 0; p < 4; p++) { int dindex = trie[j].next[p]->index; dp[v][dindex][k|trie[dindex].num] = true; } } } u = v; } int ans = -1; for(int i = 0; i <= cnt; i++) for(int j = 0; j < (1<<n); j++) { if(dp[u][i][j] == false) continue; ans = max(ans, sval[j]); } if(ans != -1) printf("%d\n", ans); else printf("No Rabbit after 2012!\n"); } return 0; }