AC自动机其实就是 字典树+KMP。 最开始的时候我没有刷字典树的题目,所以不是很能理解, 但是刷完字典树之后,对AC自动机得理解就比较简单了。
AC自动机的构造中有3个需要注意的地方:
1、next[i][j] 这个next里面存的值和字典树的是几乎一样的(不过也有些不同,下面我会解释的)。存放的是当前所在第i个节点当下一个出现的字符为j的时候的节点位置。
2、fail[i] 这个和kmp中fail是一样的,当前第i个节点失配的话,那就去尝试匹配第fail[i] 里面存放的节点。
**以上就是字典树与kmp的合体,不过不同的是:1、在字典树中如果当前节点为i的话, 如果没有为j的节点, 那么next[i][j] == -1; 但是在AC自动机中里面会存放是,当前i节点的失配指针指向的节点中如果有同样j的节点那么就会next[i][j]指向它,如果其失配指针(fail[i])没有j节点的,那么就找fail[fail[i]] 中的j节点是否存在, 直到一直找到根为止。
2、在构建fail[i]的时候用的是这样的思路(BFS),如果在当前节点失配,那么就去找当前节点的父亲节点的失配指针指向的节点中是否有i节点,如果有就指向它,否则就在去找它的失配节点, 一直到找到或者到根为止。**
3、temp指针,其实在AC自动机里面每次沿着字典树走,每当走到一个新的节点,就会沿着当前节点的失配节点一直往上走,看是否有匹配的节点;
比如 字典树中有这两个字符 acdm, cd.
现在要求匹配的acdmk, 如果没有temp的话就只会找到acdm,但是有了temp指针,就能当走到acdm中的d的时候就可以匹配到cd了。
本人较懒没有附加图片,多多画图,和体会一下,应该就可以懂了的。
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
#include <queue>
#include <cmath>
#include <math.h>
#include <stack>
#include <vector>
using namespace std;
#define Max_N 500010
struct Trie
{
int next[Max_N][26], fail[Max_N], end1[Max_N];
int root, L;
int newnode()
{
for (int i = 0; i < 26; i++)
next[L][i] = -1;
end1[L++] = 0;
return L-1;
}
void init()
{
L = 0;
root = newnode();
}
void insert(char buf[])
{
int len = strlen(buf);
int now = root;
for (int i = 0; i < len; i++) {
if (next[now][buf[i] - 'a'] == -1)
next[now][buf[i]-'a'] = newnode();
now = next[now][buf[i] - 'a'];
}
end1[now]++;
}
void build()
{
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 now = q.front();
q.pop();
for (int i = 0; i < 26; 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++) {
now = next[now][buf[i] - 'a'];
int temp = now;
while (temp != root) {
res += end1[temp];
end1[temp] = 0;
temp = fail[temp];
}
}
return res;
}
};
char buf[1000010];
Trie ac;
int main()
{
int T;
int n;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
ac.init();
for (int i = 0; i < n; i++) {
scanf("%s", buf);
ac.insert(buf);
}
ac.build();
scanf("%s", buf);
printf("%d\n", ac.query(buf));
}
return 0;
}