AC自动机
看这数据结构的名字就必须学习一个啊~~
AC自动机就是Trie和kmp的结合。
kmp是查询一个字符串,而自动机用于多个字符串的查询,比如给一篇文章和许多字符串,问有多少字符串出现过等。
由于是一般是模板题,先上板啦,在kuangbin大大那里搬过来的。
是HDU2222 的AC代码
#include <stdio.h> #include <algorithm> #include <iostream> #include <string.h> #include <queue> using namespace std; const int N = 500010; // 字符串个数*字符串长度 const int A = 26; // 不同字符个数 const int M = 10000; // 字符串个数 struct ACAutomata { int next[N][A], fail[N], end[N]; int root, L; int newNode() { for (int i = 0; i < A; ++i) next[L][i] = -1; end[L] = 0; return L++; } void init() { L = 0; root = newNode(); } void insert(char buf[]) { int len = strlen(buf); int now = root; for (int i = 0; i < len; ++i) { int ch = buf[i] - 'a'; if (next[now][ch] == -1) next[now][ch] = newNode(); now = next[now][ch]; } end[now]++; } void build() { queue<int> Q; fail[root] = root; for (int i = 0; i < A; ++i) { if (next[root][i] == -1) { next[root][i] = root; } else { fail[ next[root][i] ] = root; Q.push( next[root][i] ); } } while (Q.size()) { int now = Q.front(); Q.pop(); for (int i = 0; i < A; ++i) { if (next[now][i] == -1) { next[now][i] = next[ fail[now] ][i]; } else { fail[ next[now][i] ] = next[ fail[now] ][i]; Q.push(next[now][i]); } } } } int query(char buf[]) { int len = strlen(buf); int now = root; int res = 0; for (int i = 0; i < len; ++i) { int ch = buf[i] - 'a'; now = next[now][ch]; int tmp = now; while (tmp != root) { res += end[tmp]; end[tmp] = 0; tmp = fail[tmp]; } } return res; } } ac; char buf[1000010]; int main() { int T, n; scanf("%d", &T); while (T--) { scanf("%d", &n); ac.init(); while (n--) { scanf("%s", buf); ac.insert(buf); } scanf("%s", buf); ac.build(); printf("%d\n", ac.query(buf)); } return 0; }
L就是AC自动机的状态数。
以bfs的顺序遍历trie。
fail适配指针,fail[i]指第i个状态的下一个失配后转移的位置。没有可以转移的位置就为root。
end[i]表示以状态i为结尾的字符串个数。
query()里面的while用来求重复的字符串,例如she包含he。
类似模板题HDU2896 和 HDU3065 因为没有相等的字符串,end[i]来记录以i结束的字符串id就可以了。