1. 程式人生 > >hdu 3247(ac自動機+狀態壓縮dp+最短路)

hdu 3247(ac自動機+狀態壓縮dp+最短路)

題意:有n個原始檔也就是n個字串(n<=10 長度<=1000),m種病毒也同樣是m個字串(m<=1000,總長度50000),現在要問最短的串包含所有原始檔但不存在一個子串是病毒的長度,原始檔的串可以重疊存在,所有字串由01組成。
題解:好題,思路來自這裡http://m.blog.csdn.net/blog/woshi250hua/8021283
問題可以轉化為找出一個最短的串,包含所有原始檔字串且每個字串可重疊出現僅一次,但不包含任何一個病毒字串。因為原始檔字串最多10個,可以用狀態壓縮,每個位0表示沒有出現,1表示出現。先把兩種模式串都存到trie圖中,val[i]表示存節點i的對應原始檔狀態,如果是病毒存成-1。f[s][i]是狀態是s新增第i個原始檔字串的情況下最短串長度,狀態轉移f[s | val[flag[k]]][k] = min(f[s | val[flag[k]]][k], f[s][j] + dis[j][k])。這裡flag[k]存第k個原始檔節點編號,dis[j][k]是從第j個原始檔跳轉到第k個原始檔的最小步數,用求最短路的方式求得。為什麼是這個跳轉的最小步數,是因為在trie圖中,假如當前節點是i,它的fail指標指向的另一個節點j,那麼從根節點到節點j的這個字首串一定是根節點到節點i的一個字尾串,且一定是最長字尾

,所以dis[j][k]這條路徑是從根節點開始,先走到節點flag[j],完成了原始檔flag[j]串,最後通過fail指標走到節點flag[k],整條路徑剛好是包含了兩個重疊的原始檔串,自己畫圖能更好理解。所以預處理出所有節點到其他節點的”最短路”就能優化求最短長度符合要求的串。最後在f[(1 << n) - 1][i]中找最小值就是解。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const
int N = 60005; const int INF = 0x3f3f3f3f; int Next[N][2], val[N], fail[N], sz, n, m; int flag[11], f[1030][11], cnt, d[N], vis[N], mindis[11][11]; char str[N]; void init() { memset(Next[0], 0, sizeof(Next[0])); val[0] = 0; sz = 1; } void insert(char *s, int v) { int u = 0, len = strlen(s); for
(int i = 0; i < len; i++) { int k = s[i] - '0'; if (!Next[u][k]) { memset(Next[sz], 0, sizeof(Next[sz])); val[sz] = 0; Next[u][k] = sz++; } u = Next[u][k]; } val[u] = v; } void getFail() { queue<int> Q; fail[0] = 0; for (int i = 0; i < 2; i++) if (Next[0][i]) { fail[Next[0][i]] = 0; Q.push(Next[0][i]); } while (!Q.empty()) { int u = Q.front(); Q.pop(); if (val[fail[u]] == -1) val[u] = -1; else val[u] |= val[fail[u]]; for (int i = 0; i < 2; i++) { if (!Next[u][i]) Next[u][i] = Next[fail[u]][i]; else { fail[Next[u][i]] = Next[fail[u]][i]; Q.push(Next[u][i]); } } } } void spfa(int s) { queue<int> Q; memset(d, INF, sizeof(d)); memset(vis, 0, sizeof(vis)); d[s] = 0; vis[s] = 1; Q.push(s); while (!Q.empty()) { int u = Q.front(); Q.pop(); vis[u] = 0; for (int i = 0; i < 2; i++) { if (d[Next[u][i]] > d[u] + 1 && val[Next[u][i]] >= 0) { d[Next[u][i]] = d[u] + 1; if (!vis[Next[u][i]]) { Q.push(Next[u][i]); vis[Next[u][i]] = 1; } } } } } int solve() { flag[0] = 0; cnt = 1; for (int i = 0; i < sz; i++) if (val[i] > 0) flag[cnt++] = i; for (int i = 0; i < cnt; i++) { spfa(flag[i]); for (int j = 0; j < cnt; j++) mindis[i][j] = d[flag[j]]; } int all = 1 << n; memset(f, INF, sizeof(f)); f[0][0] = 0; for (int i = 0; i < all; i++) for (int j = 0; j < cnt; j++) if (f[i][j] < INF) { for (int k = 0; k < cnt; k++) { if (mindis[j][k] == INF || j == k) continue; f[i | val[flag[k]]][k] = min(f[i | val[flag[k]]][k], f[i][j] + mindis[j][k]); } } int res = INF; for (int i = 0; i < cnt; i++) res = min(res, f[all - 1][i]); return res; } int main() { while (scanf("%d%d", &n, &m) == 2 && n + m) { init(); for (int i = 0; i < n; i++) { scanf("%s", str); insert(str, 1 << i); } for (int i = 0; i < m; i++) { scanf("%s", str); insert(str, -1); } getFail(); printf("%d\n", solve()); } return 0; }