poj3341
AC自动机,用40^4 * 50 * 10的空间进行dp。
最大的难点在于hash。
hash一个数列f,数列中的每一位都有一个上限g,即f[i]<=g[i]。
那么可以将该数列hash为这样一个整数,这个整数的每一个位的进制都不同,第i位的进制是g[i] + 1,即第i位满g[i]+1则可进位。(当然由于g[i]是该位的上限,所以永远不可能进位)
用p[i]表示(g[0]+1)*(g[1]+1)*...*(g[i - 1]+1)。那么最终f被hash的结果是p[0]*f[0]+p[1]*f[1]+...。
#include <cstdio> #include <algorithm> #include <queue> #include <map> #include <cstring> using namespace std; #define D(x) const int MAX_LEN = 50; const int MAX_N = 55; const int MAX_NODE_NUM = 12 * MAX_N; const int MAX_CHILD_NUM = 4; //1.init() 2.insert() 3.build() 4.query() struct Trie { int next[MAX_NODE_NUM][MAX_CHILD_NUM]; int fail[MAX_NODE_NUM]; int count[MAX_NODE_NUM]; int node_cnt; bool vis[MAX_NODE_NUM]; //set it to false int root; void init() { node_cnt = 0; root = newnode(); memset(vis, 0, sizeof(vis)); } int newnode() { for (int i = 0; i < MAX_CHILD_NUM; i++) next[node_cnt][i] = -1; count[node_cnt++] = 0; return node_cnt - 1; } int get_id(char a) { if (a == 'A') return 0; if (a == 'C') return 1; if (a == 'T') return 2; return 3; } void insert(char buf[]) { int now = root; for (int i = 0; buf[i]; i++) { int id = get_id(buf[i]); if (next[now][id] == -1) next[now][id] = newnode(); now = next[now][id]; } count[now]++; } void build() { queue<int>Q; fail[root] = root; for (int i = 0; i < MAX_CHILD_NUM; i++) if (next[root][i] == -1) next[root][i] = root; else { fail[next[root][i]] = root; Q.push(next[root][i]); } while (!Q.empty()) { int now = Q.front(); Q.pop(); for (int i = 0; i < MAX_CHILD_NUM; i++) if (next[now][i] == -1) next[now][i] = next[fail[now]][i]; else { fail[next[now][i]]=next[fail[now]][i]; count[next[now][i]] += count[fail[next[now][i]]]; Q.push(next[now][i]); } } } int query(char buf[]) { int now = root; int res = 0; memset(vis, 0, sizeof(vis)); for (int i = 0; buf[i]; i++) { now = next[now][get_id(buf[i])]; int temp = now; while (temp != root && !vis[temp]) { res += count[temp]; // optimization: prevent from searching this fail chain again. //also prevent matching again. vis[temp] = true; temp = fail[temp]; } } return res; } void debug() { for(int i = 0;i < node_cnt;i++) { printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],count[i]); for(int j = 0;j < MAX_CHILD_NUM;j++) printf("%2d",next[i][j]); printf("]\n"); } } }ac; char st[MAX_LEN]; int n; int num[4]; int num2[4]; int dp[15000][505]; int myhash(int f[]) { int ret = 0; for (int i = 0; i < 4; i++) { ret += f[i] * num[i]; } return ret; } int work() { int temp[4] = {0}; for (int i = 0; st[i]; i++) { temp[ac.get_id(st[i])]++; } num[3] = 1; for (int i = 2; i >= 0; i--) { num[i] = (temp[i + 1] + 1) * num[i + 1]; } memset(dp, -1, sizeof(dp)); int f[4]; int g[4]; int ret = 0; for (f[0] = 0; f[0] <= temp[0]; f[0]++) for (f[1] = 0; f[1] <= temp[1]; f[1]++) for (f[2] = 0; f[2] <= temp[2]; f[2]++) for (f[3] = 0; f[3] <= temp[3]; f[3]++) { for (int u = 0; u < ac.node_cnt; u++) { int h = myhash(f); for (int j = 0; j < 4; j++) { g[j] = f[j]; } int temp2 = 0; for (int j = 0; j < 4; j++) { g[j]--; int h2 = myhash(g); int v = ac.next[u][j]; if (g[j] >= 0) { temp2 = max(temp2, dp[h2][v]); D(printf("\t\t\t%d %d %d %d %d %d %d\n", g[0], g[1], g[2], g[3], v, temp2, h2)); } g[j]++; } dp[h][u] = temp2 + ac.count[u]; ret = max(ret, dp[h][u]); D(printf("%d %d %d %d %d %d %d\n", f[0], f[1], f[2], f[3], u, dp[h][u], h)); } } return dp[myhash(temp)][ac.root]; } int main() { int t = 0; while (scanf("%d", &n), n) { ac.init(); for (int i = 0; i < n; i++) { scanf("%s", st); ac.insert(st); } ac.build(); scanf("%s", st); printf("Case %d: %d\n", ++t, work()); } return 0; }