HDU 4057:Rescue the Rabbit(AC自动机+状压DP)***

http://acm.hdu.edu.cn/showproblem.php?pid=4057

题意:给出n个子串,串只包含‘A’,'C','G','T'四种字符,你现在需要构造出一个长度为l的串,如果这个串里面包含了某个子串,那么答案就会+val[i](如果这个串被使用过了,就不会再有贡献了),要使得构造出来的串的答案最大,问是多少。

思路:只能想到是AC自动机的题目,然后乱搞出一个错误的方法,没想到是这样的操作。

n只有10,因此才1024,开一个dp[l][sz][st]的数组,l代表当前的字符串长度,sz代表当前遍历的结点,st代表当前的状态(二进制表示使用了哪些字符串),这里的长度用滚动数组优化。

方程:dp[now][nxt[j][x]][k|val[nxt[j][x]]] = dp[pre][j][k]; (当且仅当dp[pre][j][k]状态可取)

nxt[j][x]代表j结点的下一个状态,val[now]代表now这个结点是哪个字符串的终点,因此k|val[nxt[j][x]]就是当前拥有的字符串的状态+下一个结点拥有的字符串的状态。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <queue>
 5 using namespace std;
 6 #define N 1010
 7 #define INF 0x3f3f3f3f
 8 int nxt[N][4], fail[N], val[N], rt, sz, n, l, num[11];
 9 bool dp[2][N][(1<<10)+10];
10 char s[105];
11 int newnode() {
12     for(int i = 0; i < 4; i++) nxt[sz][i] = -1;
13     val[sz++] = 0;
14     return sz - 1;
15 }
16 void init() {
17     sz = 0; rt = newnode();
18 }
19 int Turn(char c) {
20     if(c == 'A') return 0;
21     if(c == 'C') return 1;
22     if(c == 'G') return 2;
23     return 3;
24 }
25 void Insert(int id) {
26     int len = strlen(s);
27     int now = rt;
28     for(int i = 0; i < len; i++) {
29         int c = Turn(s[i]);
30         if(nxt[now][c] == -1) nxt[now][c] = newnode();
31         now = nxt[now][c];
32     }
33     val[now] |= (1 << id);
34 }
35 void Build() {
36     queue<int> que;
37     while(!que.empty()) que.pop();
38     fail[rt] = rt;
39     for(int i = 0; i < 4; i++) {
40         if(nxt[rt][i] == -1) nxt[rt][i] = rt;
41         else fail[nxt[rt][i]] = rt, que.push(nxt[rt][i]);
42     }
43     while(!que.empty()) {
44         int now = que.front(); que.pop();
45         val[now] |= val[fail[now]]; // 如果fail指向的结点是有状态的,应该加上!!!
46         for(int i = 0; i < 4; i++) {
47             if(nxt[now][i] == -1) nxt[now][i] = nxt[fail[now]][i];
48             else fail[nxt[now][i]] = nxt[fail[now]][i], que.push(nxt[now][i]);
49         }
50     }
51 }
52 void Solve() {
53     int pre = 0, now = 1;
54     memset(dp, 0, sizeof(dp));
55     dp[pre][0][0] = 1;
56     for(int i = 0; i < l; i++) {
57         memset(dp[now], 0, sizeof(dp[now]));
58         for(int j = 0; j < sz; j++) {
59             for(int k = 0; k < (1 << n); k++) {
60                 if(!dp[pre][j][k]) continue;
61                 for(int x = 0; x < 4; x++) {
62                     dp[now][nxt[j][x]][k|val[nxt[j][x]]] = dp[pre][j][k]; // 哪些状态可以取
63                     // nxt[j][x] 下一个结点
64                     // k | val[nxt[j][x]] 当前状态加上下一个结点有的状态
65                 }
66             }
67         }
68         swap(now, pre);
69     }
70     int ans = -INF, res; now = l % 2;
71     for(int i = 0; i < sz; i++) {
72         for(int j = 0; j < (1 << n); j++) {
73             if(!dp[now][i][j]) continue;
74             res = 0;
75             for(int k = 0; k < n; k++)
76                 if(j & (1 << k)) res += num[k];
77             ans = ans > res ? ans : res;
78         }
79     }
80     if(ans >= 0) printf("%d
", ans);
81     else puts("No Rabbit after 2012!");
82 }
83 
84 int main() {
85     while(~scanf("%d%d", &n, &l)) {
86         init();
87         for(int i = 0; i < n; i++) scanf("%s %d", s, &num[i]), Insert(i);
88         Build();
89         Solve();
90     }
91     return 0;
92 }
93 
94 /*
95 3 2
96 A -1
97 G -1
98 C -1
99 */
原文地址:https://www.cnblogs.com/fightfordream/p/6828651.html