我是靠谱客的博主 苗条小笼包,这篇文章主要介绍UVA - 1076 || LA 4126 Password Suspects (AC自动机 + 状压DP + 打印解),现在分享给大家,希望可以做个参考。
题意:
让你构造一个长度为n 的串, 告诉你m个串, 要求长度为n 的串 必须包含m 个串, 问你有多少种方案。如果方案数 <= 42 输出所有解。
思路:
不看输出方案。
很明显一个自动机 + 状压的题目。
令dp[i][j][k] 表示 构造字符串的第i 位, 目前在自动机的j 结点, 包含m 个串的状态为k 的方案数。那么直接转移就好了。
但是要打印解 。
感觉这里比较乱 想了好久。
因为自己写的dp 都是正着推的, 从dp[0][0][0] 推到终点。 但是这样不好打印解。
于是改成了记忆话搜索, 从终点向前推。
这样直接从起点 在dfs 一遍就能打印解了, 直接看当前的dp 值是不是大于0 即可, 大于0 就继续递归 ,否则return。
注意:
可能会有重复单词的出现。
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159#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 suspectsn", ans); if (ans <= 42){ find(0,0,0,"", n, m); for (int i = 0; i < v.size(); ++i){ printf("%sn", 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; }
最后
以上就是苗条小笼包最近收集整理的关于UVA - 1076 || LA 4126 Password Suspects (AC自动机 + 状压DP + 打印解)的全部内容,更多相关UVA内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复