AC自动机专题
AC自动机简介:KMP是用于解决单模式串匹配问题, AC自动机用于解决多模式串匹配问题。
精华:设这个节点上的字母为C,沿着他父亲的失败指针走,直到走到一个节点,他的儿子中也有字母为C的节点。然后把当前节点的失败指针指向那个字目也为C的儿子。如果一直走到了root都没找到,那就把失败指针指向root。
如果用KMP来解决多模式串匹配问题,则复杂度为O(n + k * m), 而AC自动机的负责度为O(n + m + z), z为模式串出现的次数。
学习链接:
http://hi.baidu.com/nialv7/item/ce1ce015d44a6ba7feded52d
http://blog.csdn.net/niushuai666/article/details/7002823
http://www.cnblogs.com/kuangbin/p/3164106.html
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2222
思路:AC自动机的入门题,用的是bin牛的模板,统计End数组即可,统计过的需要清0.
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <queue> 6 #define FOR(i, a, b) for (int i = (a); i < (b); ++i) 7 #define REP(i, a, b) for (int i = (a); i <= (b); ++i) 8 using namespace std; 9 10 const int MAX_N = (500000 + 500); 11 struct Trie { 12 int next[MAX_N][26], End[MAX_N], fail[MAX_N]; 13 int root, L; 14 int NewNode() 15 { 16 FOR(i, 0, 26) next[L][i] = -1; 17 End[L++] = 0; 18 return L - 1; 19 } 20 void Init() 21 { 22 L = 0; 23 root = NewNode(); 24 } 25 void Insert(char *str) 26 { 27 int len = strlen(str), now = root; 28 FOR(i, 0, len) { 29 int id = str[i] - 'a'; 30 if (next[now][id] == -1) next[now][id] = NewNode(); 31 now = next[now][id]; 32 } 33 ++End[now]; 34 } 35 void Build() 36 { 37 queue<int > que; 38 fail[root] = root; 39 FOR(i, 0, 26) { 40 if (next[root][i] == -1) next[root][i] = root; 41 else { 42 fail[next[root][i]] = root; 43 que.push(next[root][i]); 44 } 45 } 46 while (!que.empty()) { 47 int now = que.front(); 48 que.pop(); 49 FOR(i, 0, 26) { 50 if (next[now][i] == -1) { 51 next[now][i] = next[fail[now]][i]; 52 } else { 53 fail[next[now][i]] = next[fail[now]][i]; 54 que.push(next[now][i]); 55 } 56 } 57 } 58 } 59 int Query(char *str) 60 { 61 int len = strlen(str), now = root, res = 0; 62 FOR(i, 0, len) { 63 int id = str[i] - 'a'; 64 now = next[now][id]; 65 int tmp = now; 66 while (tmp != root) { 67 res += End[tmp]; 68 End[tmp] = 0; 69 tmp = fail[tmp]; 70 } 71 } 72 return res; 73 } 74 } AC; 75 76 int n; 77 char str[1000000 + 100]; 78 79 int main() 80 { 81 int Cas; 82 scanf("%d", &Cas); 83 while (Cas--) { 84 AC.Init(); 85 scanf("%d", &n); 86 REP(i, 1, n) { 87 scanf("%s", str); 88 AC.Insert(str); 89 } 90 AC.Build(); 91 scanf("%s", str); 92 printf("%d\n", AC.Query(str)); 93 } 94 return 0; 95 }
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2896
思路:和上题差不多,只是用End数组来记录序号而已。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <queue> 6 #include <vector> 7 #define FOR(i, a, b) for (int i = (a); i < (b); ++i) 8 #define REP(i, a, b) for (int i = (a); i <= (b); ++i) 9 using namespace std; 10 11 const int MAX_N = (100000 + 1000); 12 struct Trie { 13 14 int next[MAX_N][128], End[MAX_N], fail[MAX_N]; 15 int root, L; 16 int NewNode() { 17 FOR(i, 0, 128) next[L][i] = -1; 18 End[L++] = 0; 19 return L - 1; 20 } 21 void Init() { 22 L = 0; 23 root = NewNode(); 24 } 25 26 void Insert(char *str, int index) { 27 int len = strlen(str), now = root; 28 FOR(i, 0, len) { 29 int id = str[i]; 30 if (next[now][id] == -1) next[now][id] = NewNode(); 31 now = next[now][id]; 32 } 33 End[now] = index; 34 } 35 void Build() { 36 queue<int > que; 37 fail[root] = root; 38 FOR(i, 0, 128) { 39 if (next[root][i] == -1) next[root][i] = root; 40 else { 41 fail[next[root][i]] = root; 42 que.push(next[root][i]); 43 } 44 } 45 while (!que.empty()) { 46 int now = que.front(); 47 que.pop(); 48 FOR(i, 0, 128) { 49 if (next[now][i] == -1) { 50 next[now][i] = next[fail[now]][i]; 51 } else { 52 fail[next[now][i]] = next[fail[now]][i]; 53 que.push(next[now][i]); 54 } 55 } 56 } 57 } 58 void Query(char *str, vector<int > &ans) { 59 int len = strlen(str), now = root; 60 FOR(i, 0, len) { 61 now = next[now][str[i]]; 62 int tmp = now; 63 while (tmp != root) { 64 if (End[tmp]) ans.push_back(End[tmp]); 65 tmp = fail[tmp]; 66 } 67 } 68 } 69 70 } AC; 71 72 int N, M, res; 73 char str[10000 + 100]; 74 vector<int > ans[1000 + 100]; 75 76 int main() 77 { 78 AC.Init(); 79 scanf("%d", &N); 80 REP(i, 1, N) { 81 scanf("%s", str); 82 AC.Insert(str, i); 83 } 84 AC.Build(); 85 scanf("%d", &M); 86 FOR(i, 0, M) { 87 scanf("%s", str); 88 AC.Query(str, ans[i]); 89 } 90 res = 0; 91 FOR(i, 0, M) { 92 if ((int)ans[i].size()) { 93 printf("web %d:", i + 1); 94 sort(ans[i].begin(), ans[i].end()); 95 FOR(j, 0, (int)ans[i].size()) printf(" %d", ans[i][j]); 96 puts(""); 97 ++res; 98 } 99 } 100 printf("total: %d\n", res); 101 return 0; 102 }
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3065
思路:用一个数组来记录模式串在主串中出现的次数。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <queue> 6 #define FOR(i, a, b) for (int i = (a); i < (b); ++i) 7 #define REP(i, a, b) for (int i = (a); i <= (b); ++i) 8 using namespace std; 9 10 const int MAX_N = (50000 + 500); 11 12 int N, num[1000 + 100]; 13 char ss[1000 + 100][55]; 14 char str[2000000 + 200]; 15 16 struct Trie { 17 int next[MAX_N][128], End[MAX_N], fail[MAX_N]; 18 int root, L; 19 int NewNode() { 20 FOR(i, 0, 128) next[L][i] = -1; 21 End[L++] = -1; 22 return L - 1; 23 } 24 25 void Init() { 26 L = 0; 27 root = NewNode(); 28 } 29 30 void Insert(char *str, int index) { 31 int len = strlen(str), now = root; 32 FOR(i, 0, len) { 33 if (next[now][str[i]] == -1) next[now][str[i]] = NewNode(); 34 now = next[now][str[i]]; 35 } 36 End[now] = index; 37 } 38 39 void Build() { 40 queue<int > que; 41 fail[root] = root; 42 FOR(i, 0, 128) { 43 if (next[root][i] == -1) next[root][i] = root; 44 else { 45 fail[next[root][i]] = root; 46 que.push(next[root][i]); 47 } 48 } 49 while (!que.empty()) { 50 int now = que.front(); 51 que.pop(); 52 FOR(i, 0, 128) { 53 if (next[now][i] == -1) next[now][i] = next[fail[now]][i]; 54 else { 55 fail[next[now][i]] = next[fail[now]][i]; 56 que.push(next[now][i]); 57 } 58 } 59 } 60 } 61 62 void Query(char *str) { 63 memset(num, 0, sizeof(num)); 64 int len = strlen(str), now = root; 65 FOR(i, 0, len) { 66 now = next[now][str[i]]; 67 int tmp = now; 68 while (tmp != root) { 69 if (End[tmp] != -1) ++num[End[tmp]]; 70 tmp = fail[tmp]; 71 } 72 } 73 FOR(i, 0, N) { 74 if (num[i]) printf("%s: %d\n", ss[i], num[i]); 75 } 76 } 77 78 } AC; 79 80 81 int main() 82 { 83 while (~scanf("%d", &N)) { 84 AC.Init(); 85 scanf("%d", &N); 86 FOR(i, 0, N) { 87 scanf("%s", ss[i]); 88 AC.Insert(ss[i], i); 89 } 90 AC.Build(); 91 scanf("%s", str); 92 AC.Query(str); 93 } 94 return 0; 95 }
题目链接:http://poj.org/problem?id=2778
思路:需要用到的知识:有向图中点A到点B走K步的路径数等于有向图原始矩阵的K次幂。然后对于已经建好的Trie图,我们就可以建图了,如果某个节点A不是终止节点并且这个节点的next节点B也不是终止节点,那么就连边(表示从A点走1步到节点B的方法有1种)。建好图之后就是矩阵的快速幂了,然后在统计节点0(根节点)到其余节点走N步的方法数的总和。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <queue> 6 #define REP(i, a, b) for (int i = (a); i < (b); ++i) 7 #define FOR(i, a, b) for (int i = (a); i <= (b); ++i) 8 using namespace std; 9 10 const int MAX_N = (100 + 10); 11 const int MOD = (100000); 12 int M, N; 13 char str[22]; 14 15 struct Matrix { 16 long long mat[MAX_N][MAX_N]; 17 int n; 18 Matrix() {} 19 Matrix(int _n) 20 { 21 n = _n; 22 REP(i, 0, n) 23 REP(j, 0, n) mat[i][j] = 0; 24 } 25 Matrix operator *(const Matrix &b) const 26 { 27 Matrix c = Matrix(n); 28 REP(i, 0, n) { 29 REP(j, 0, n) { 30 REP(k, 0, n) { 31 c.mat[i][j] += mat[i][k] * b.mat[k][j]; 32 if (c.mat[i][j] >= MOD) c.mat[i][j] %= MOD; 33 } 34 } 35 } 36 return c; 37 } 38 39 }; 40 41 Matrix Pow(Matrix mat, int n) 42 { 43 Matrix ONE = Matrix(mat.n); 44 REP(i, 0, mat.n) ONE.mat[i][i] = 1; 45 Matrix tmp = mat; 46 while (n) { 47 if (n & 1) ONE = ONE * tmp; 48 n >>= 1; 49 tmp = tmp * tmp; 50 } 51 return ONE; 52 } 53 54 struct Trie { 55 int next[MAX_N][4], End[MAX_N], fail[MAX_N]; 56 int L, root; 57 int NewNode() 58 { 59 REP(i, 0, 4) next[L][i] = -1; 60 End[L++] = 0; 61 return L - 1; 62 } 63 64 void Init() 65 { 66 L = 0; 67 root = NewNode(); 68 } 69 70 int getID(char ch) 71 { 72 if (ch == 'A') return 0; 73 if (ch == 'C') return 1; 74 if (ch == 'G') return 2; 75 if (ch == 'T') return 3; 76 } 77 78 void Insert(char *str) 79 { 80 int len = strlen(str), now = root; 81 REP(i, 0, len) { 82 int id = getID(str[i]); 83 if (next[now][id] == -1) next[now][id] = NewNode(); 84 now = next[now][id]; 85 } 86 End[now] = 1; 87 } 88 89 void Build() 90 { 91 queue<int > que; 92 fail[root] = root; 93 REP(i ,0, 4) { 94 if (next[root][i] == -1) next[root][i] = root; 95 else { 96 fail[next[root][i]] = root; 97 que.push(next[root][i]); 98 } 99 } 100 while (!que.empty()) { 101 int now = que.front(); 102 que.pop(); 103 if (End[fail[now]]) End[now] = 1; 104 REP(i, 0, 4) { 105 if (next[now][i] == -1) next[now][i] = next[fail[now]][i]; 106 else { 107 fail[next[now][i]] = next[fail[now]][i]; 108 que.push(next[now][i]); 109 } 110 } 111 } 112 } 113 114 Matrix getMatrix() 115 { 116 Matrix res = Matrix(L); 117 REP(i, 0, L) 118 REP(j, 0, 4) if (!End[next[i][j]]) ++res.mat[i][next[i][j]]; 119 return res; 120 } 121 122 } AC; 123 124 125 int main() 126 { 127 while (~scanf("%d %d", &M, &N)) { 128 AC.Init(); 129 FOR(i, 1, M) scanf("%s", str), AC.Insert(str); 130 AC.Build(); 131 Matrix tmp = AC.getMatrix(); 132 tmp = Pow(tmp, N); 133 long long ans = 0; 134 REP(i, 0, tmp.n) { 135 ans += tmp.mat[0][i]; 136 if (ans >= MOD) ans %= MOD; 137 } 138 printf("%lld\n", ans); 139 } 140 return 0; 141 }
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=224
思路:出现过给定单词的单词数 = 所有的单词数 - 没有出现过给定单词的单词数, 而所有的单词数 = 26^1 + 26^2 + ... + 26^L,没有出现过给定单词的单词数 = A + A^2 + ... + A^n,其中A是根据能走的字符之间的路径数建立的邻接矩阵。
那么如何求出A + A^2 + A^3 + ... + A^L?
可以这样做,依据等比矩阵的特点有:
于是可以很快计算出A^1+ A ^2 + ... + A ^L 以及 26^1 + 26^2 + ... + 26 ^ L的值。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #define REP(i, a, b) for (int i = (a); i < (b); ++i) #define FOR(i, a, b) for (int i = (a); i <= (b); ++i) using namespace std; const int MAX_N = (33); int N, L; typedef unsigned long long ULL; struct Matrix { ULL mat[MAX_N][MAX_N]; int n; Matrix(){} Matrix(int _n) : n(_n) { REP(i, 0, n) REP(j, 0, n) mat[i][j] = 0; } }; Matrix Mul(const Matrix &a, const Matrix &b) { Matrix c = Matrix(a.n); REP(i, 0, a.n) { REP(j, 0, a.n) REP(k, 0, a.n) c.mat[i][j] += a.mat[i][k] * b.mat[k][j]; } return c; } Matrix Pow(Matrix p,int n) { Matrix ONE = Matrix(p.n); REP(i, 0, ONE.n) ONE.mat[i][i] = 1; while (n) { if (n & 1) ONE = Mul(ONE, p); n >>= 1; p = Mul(p, p); } return ONE; } struct Trie { int next[MAX_N][MAX_N], end[MAX_N], fail[MAX_N]; int root, L; void Init() { L = 0; root = NewNode(); } int NewNode() { REP(i, 0, 26) next[L][i] = -1; end[L++] = 0; return (L - 1); } void Insert(char *str) { int len = strlen(str), now = root; REP(i, 0, len) { int index = str[i] - 'a'; if (next[now][index] == -1) next[now][index] = NewNode(); now = next[now][index]; } end[now] = 1; } void Build() { queue<int > que; fail[root] = root; REP(i, 0, 26) { if (next[root][i] == -1) next[root][i] = root; else { fail[next[root][i]] = root; que.push(next[root][i]); } } while (!que.empty()) { int now = que.front(); que.pop(); if (end[fail[now]]) end[now] = 1; REP(i, 0, 26) { if (next[now][i] == -1) next[now][i] = next[fail[now]][i]; else { fail[next[now][i]] = next[fail[now]][i]; que.push(next[now][i]); } } } } Matrix getMatrix() { Matrix tmp = Matrix(L + 1); REP(i, 0, L) { REP(j, 0, 26) if (!end[next[i][j]]) ++tmp.mat[i][next[i][j]]; } REP(i, 0, L + 1) tmp.mat[i][L] = 1; return tmp; } } AC; int main() { while (cin >> N >> L) { AC.Init(); FOR(i, 1, N) { char str[MAX_N]; cin >> str; AC.Insert(str); } AC.Build(); Matrix a = AC.getMatrix(); a = Pow(a, L); ULL ans(0); REP(i, 0, a.n) ans += a.mat[0][i]; ans -= 1; a = Matrix(2); a.mat[0][0] = 26, a.mat[0][1] = a.mat[1][1] = 1; a = Pow(a, L); ULL sum = a.mat[0][0] + a.mat[0][1]; sum -= 1; cout << (sum - ans) << endl; } return 0; }
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2825
思路:建立AC自动机,每个节点都代表一种状态,建立fail指针的时候顺便更新以该节点结尾的字符串代表的状态。
dp[i][j][k]代表最终密码长度为i,以AC自动机的位置j所在的字符结尾的,并且状态为k的密码的个数。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #define REP(i, a, b) for (int i = (a); i < (b); ++i) #define FOR(i, a, b) for (int i = (a); i <= (b); ++i) using namespace std; const int MAX_N = (10 * 10 + 10); const int MOD = (20090717); int N, M, K; int num[1 << 11], dp[27][MAX_N][1 << 11]; //dp[i][j][k]表示长度为i,在AC自动机上的节点为L,并且状态为k的符合条件的个数 struct Trie { int next[MAX_N][26], end[MAX_N], fail[MAX_N]; int root, L; void Init() { L = 0; root = NewNode(); } int NewNode() { REP(i, 0, 26) next[L][i] = -1; end[L++] = 0; return (L - 1); } void Insert(char *str, int ID) { int len = strlen(str), now = root; REP(i, 0, len) { int index = str[i] - 'a'; if (next[now][index] == -1) next[now][index] = NewNode(); now = next[now][index]; } end[now] |= (1 << ID); } void Build() { queue<int > que; fail[root] = root; REP(i, 0, 26) { if (next[root][i] == -1) next[root][i] = root; else { fail[next[root][i]] = root; que.push(next[root][i]); } } while (!que.empty()) { int now = que.front(); que.pop(); end[now] |= end[fail[now]]; REP(i, 0, 26) { if (next[now][i] == -1) next[now][i] = next[fail[now]][i]; else { fail[next[now][i]] = next[fail[now]][i]; que.push(next[now][i]); } } } } int getDp() { FOR(i, 0, N) { REP(j, 0, L) REP(s, 0, (1 << M)) dp[i][j][s] = 0; } dp[0][0][0] = 1; REP(i, 0, N) { REP(j, 0, L) { REP(s, 0, (1 << M)) if (dp[i][j][s]) { REP(k, 0, 26) { dp[i + 1][next[j][k]][s | end[next[j][k]]] += dp[i][j][s]; if (dp[i + 1][next[j][k]][s | end[next[j][k]]] >= MOD) dp[i + 1][next[j][k]][s | end[next[j][k]]] %= MOD; } } } } int ans = 0; REP(s, 0, (1 << M)) if (num[s] >= K) { REP(i, 0, L) { ans += dp[N][i][s]; if (ans >= MOD) ans %= MOD; } } return ans; } } AC; int main() { memset(num, 0, sizeof(num)); REP(s, 0, (1 << 12)) { REP(i, 0, 12) if (s & (1 << i)) ++num[s]; } while (cin >> N >> M >> K) { if (N == 0 && M == 0 && K == 0) break; AC.Init(); REP(i, 0, M) { char str[11]; cin >> str; AC.Insert(str, i); } AC.Build(); printf("%d\n",AC.getDp()); } return 0; }
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2296
思路:建立Trie, end节点代表的该串的价值,dp[i][j]表示长度为i,在自动机上的位置为j的子串对应的价值。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <string> #include <queue> #define REP(i, a, b) for (int i = (a); i < (b); ++i) #define FOR(i, a, b) for (int i = (a); i <= (b); ++i) using namespace std; const int MAX_N = (1100 + 100); int N, M, val[110]; int dp[55][MAX_N]; //dp[i][j] : 长度为i,以AC自动机上的位置j的字符为结尾的串的最大价值 string str[55][MAX_N]; bool cmp(const string &str1, const string &str2) { if ((int)str1.size() != (int)str2.size()) return (int)str1.size() < (int)str2.size(); return str1 < str2; } struct Trie { int next[MAX_N][26], end[MAX_N], fail[MAX_N]; int root, L; void Init() { L = 0; root = NewNode(); } int NewNode() { REP(i, 0, 26) next[L][i] = -1; end[L++] = -1; return (L - 1); } void Insert(char *str, int ID) { int len = strlen(str), now = root; REP(i, 0, len) { int index = str[i] - 'a'; if (next[now][index] == -1) next[now][index] = NewNode(); now = next[now][index]; } end[now] = ID; } void Build() { queue<int > que; fail[root] = root; REP(i, 0, 26) { if (next[root][i] == -1) next[root][i] = root; else { fail[next[root][i]] = root; que.push(next[root][i]); } } while (!que.empty()) { int now = que.front(); que.pop(); REP(i, 0, 26) { if (next[now][i] == -1) next[now][i] = next[fail[now]][i]; else { fail[next[now][i]] = next[fail[now]][i]; que.push(next[now][i]); } } } } void getAns() { string ans(""); int maxval = 0, vv = 0; memset(dp, -1, sizeof(dp)); dp[0][0] = 0; str[0][0] = ""; REP(i, 0, N) { REP(j, 0, L) if (dp[i][j] >= 0) { REP(k, 0, 26) { string tmp = str[i][j]; tmp += (char)('a' + k); vv = dp[i][j]; if (end[next[j][k]] != -1) vv += val[end[next[j][k]]]; if (dp[i + 1][next[j][k]] < vv || (dp[i + 1][next[j][k]] == vv && cmp(tmp, str[i + 1][next[j][k]]))) { dp[i + 1][next[j][k]] = vv; str[i + 1][next[j][k]] = tmp; if (vv > maxval || (vv == maxval && cmp(tmp, ans))) { ans = tmp; maxval = vv; } } } } } cout << ans << endl; } } AC; int main() { int Cas; cin >> Cas; while (Cas--) { cin >> N >> M; AC.Init(); REP(i, 0, M) { char str[20]; cin >> str; AC.Insert(str, i); } REP(i, 0, M) cin >> val[i]; AC.Build(); AC.getAns(); } return 0; }
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2457
思路:dp[i][j]表示长度为i,在AC自动机上的位置为j满足条件的所需替换的字符的个数的最小值。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <string> #include <queue> #define REP(i, a, b) for (int i = (a); i < (b); ++i) #define FOR(i, a, b) for (int i = (a); i <= (b); ++i) using namespace std; const int MAX_N = (1000 + 10); template < typename T > inline T getMIN(const T &a, const T &b) { return a < b ? a : b; } int N, dp[MAX_N][MAX_N]; //len, pos; char str[MAX_N]; struct Trie { int next[MAX_N][4], end[MAX_N], fail[MAX_N]; int L, root; void Init() { L = 0; root = NewNode(); } int NewNode() { REP(i, 0, 4) next[L][i] = -1; end[L++] = 0; return (L - 1); } int getchange(char ch) { if (ch == 'A') return 0; if (ch == 'C') return 1; if (ch == 'G') return 2; if (ch == 'T') return 3; return -1; } void Insert(char *str) { int len = strlen(str), now = root; REP(i, 0, len) { int id = getchange(str[i]); if (next[now][id] == -1) next[now][id] = NewNode(); now = next[now][id]; } end[now] = 1; } void Build() { queue<int > que; fail[root] = root; REP(i, 0, 4) { if (next[root][i] == -1) next[root][i] = root; else { fail[next[root][i]] = root; que.push(next[root][i]); } } while (!que.empty()) { int now = que.front(); que.pop(); if (end[fail[now]]) end[now] = 1; REP(i, 0, 4) { if (next[now][i] == -1) next[now][i] = next[fail[now]][i]; else { fail[next[now][i]] = next[fail[now]][i]; que.push(next[now][i]); } } } } int getDp(char *str) { memset(dp, 0x3f, sizeof(dp)); int len = strlen(str); dp[0][0] = 0; REP(i, 0, len) { REP(j, 0, L) if (!end[j] && dp[i][j] < 0x3f3f3f3f){ REP(k, 0, 4) if (!end[next[j][k]]) { int val = (k != getchange(str[i])); dp[i + 1][next[j][k]] = getMIN(dp[i + 1][next[j][k]], dp[i][j] + val); } } } int ans = 0x3f3f3f3f; REP(i, 0, L) ans = getMIN(ans, dp[len][i]); if (ans == 0x3f3f3f3f) return -1; return ans; } } AC; int main() { int t = 1; while (cin >> N && N) { AC.Init(); REP(i, 0, N) { char ss[22]; cin >> ss; AC.Insert(ss); } cin >> str; AC.Build(); printf("Case %d: %d\n", t++, AC.getDp(str)); } return 0; }