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.

AC自动机专题 AC自动机专题
 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 }
View Code

 

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2896

思路:和上题差不多,只是用End数组来记录序号而已。

AC自动机专题 AC自动机专题
  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 }
View Code

 

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3065

思路:用一个数组来记录模式串在主串中出现的次数。

AC自动机专题 AC自动机专题
 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 }
View Code

 

 题目链接:http://poj.org/problem?id=2778

思路:需要用到的知识:有向图中点A到点B走K步的路径数等于有向图原始矩阵的K次幂。然后对于已经建好的Trie图,我们就可以建图了,如果某个节点A不是终止节点并且这个节点的next节点B也不是终止节点,那么就连边(表示从A点走1步到节点B的方法有1种)。建好图之后就是矩阵的快速幂了,然后在统计节点0(根节点)到其余节点走N步的方法数的总和。

AC自动机专题 AC自动机专题
  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?

可以这样做,依据等比矩阵的特点有:

AC自动机专题

于是可以很快计算出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;
}


 

你可能感兴趣的:(AC自动机)