1076 || LA 4126 Password Suspects (AC自動機 + 狀壓DP + 列印解)
阿新 • • 發佈:2018-12-24
題意:
讓你構造一個長度為n 的串, 告訴你m個串, 要求長度為n 的串 必須包含m 個串, 問你有多少種方案。如果方案數 <= 42 輸出所有解。
思路:
不看輸出方案。
很明顯一個自動機 + 狀壓的題目。
令dp[i][j][k] 表示 構造字串的第i 位, 目前在自動機的j 結點, 包含m 個串的狀態為k 的方案數。那麼直接轉移就好了。
但是要列印解 。
感覺這裡比較亂 想了好久。
因為自己寫的dp 都是正著推的, 從dp[0][0][0] 推到終點。 但是這樣不好列印解。
於是改成了記憶話搜尋, 從終點向前推。
這樣直接從起點 在dfs 一遍就能列印解了, 直接看當前的dp 值是不是大於0 即可, 大於0 就繼續遞迴 ,否則return。
注意:
可能會有重複單詞的出現。
#include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <string> #include <vector> using namespace std; const int maxn = 100 + 2; struct Trie{ int L, root; int next[maxn][26]; int fail[maxn]; int flag[maxn]; long long dp[26][maxn][ (1<<10)+2 ]; void init(){ L = 0; root = newnode(); } int newnode(){ for (int i = 0; i < 26; ++i){ next[L][i] = -1; } flag[L] = 0; return L++; } void insert(char* s,int ii){ int len = strlen(s); int nod = root; for (int i = 0; i < len; ++i){ int id = s[i] - 'a'; if (next[nod][id] == -1){ next[nod][id] = newnode(); } nod = next[nod][id]; } flag[nod] = ii; } void bfs(){ queue<int>q; fail[root] = root; for (int i = 0; i < 26; ++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 u = q.front(); q.pop(); flag[u] |= flag[fail[u] ]; for (int i = 0; i < 26; ++i){ if (next[u][i] == -1){ next[u][i] = next[fail[u] ][i]; } else { fail[next[u][i] ] = next[fail[u] ][i]; q.push(next[u][i]); } } } } vector<string>v; long long dfs(int i, int j, int k,int n,int m){ long long& ans = dp[i][j][k]; if (ans != -1) return ans; if (i == n){ if (k == (1<<m) - 1){ return ans = 1; } return ans = 0; } ans = 0; for (int l = 0; l < 26; ++l){ int nx = next[j][l]; char ch = 'a' + l; ans += dfs(i+1, nx, k | flag[nx], n, m); } return ans; } void find(int i,int j,int k, string p, int n, int m){ if (i >= n){ if (k == (1<<m) - 1){ v.push_back(p); } return; } if (dp[i][j][k] <= 0) return; for (int l = 0; l < 26; ++l){ int nx = next[j][l]; char ch = l + 'a'; find(i+1, nx, k | flag[nx], p + ch, n, m); } } void solve(int n, int m){ v.clear(); memset(dp,-1,sizeof dp); long long ans = dfs(0,0,0,n, m); printf("%lld suspects\n", ans); if (ans <= 42){ find(0,0,0,"", n, m); for (int i = 0; i < v.size(); ++i){ printf("%s\n", v[i].c_str()); } } } }ac; /** 4 1 hee **/ int n, m; char s[11][maxn]; int state[11]; int main(){ int ks = 0; while(~scanf("%d %d",&n, &m)){ if (n == 0 && m == 0) break; ac.init(); for (int i = 0; i < m; ++i){ scanf("%s", s[i]); state[i] = 0; } for (int i = 0; i < m; ++i){ for (int j = 0; j < m; ++j){ if (!strcmp(s[i], s[j])){ state[i] |= (1<<j); } } } for (int i = 0; i < m; ++i){ ac.insert(s[i], state[i]); } ac.bfs(); printf("Case %d: ",++ks); ac.solve(n, m); } return 0; }