AC自动机模板
Description
HDU 2222 Keywords Search AC自动机模板题
给出\(n\)个模式串,和一个文本串,问文本串中一共出现了多少个模式串。
Input
第一行给出用例组数\(T\),对于每组用例,第一行给出模式串数量\(n\),接下来的\(n\)行给出\(n\)个模式串,最后一行给出一个文本串。\(n \leqslant 10000\),每个文本串最长不超过\(50\),文本串长度不超过\(1000000\)。
Output
对于每组用例给出一个整数,表示文本串中包含的模式串的数量。
Sample Input
1
5
she
he
say
shr
her
yasherhs
Sample Output
3
Solution
AC自动机的构建关键在于fail边。fail边指向的结点的深度一定小于当前结点,故对trie树进行BFS,从上到下构造fail边。
- 初始化fail[0] = 0,fail[trie[0][i]] = 0。
- 设v = trie[u][x],且trie[fail[u][x]] != 0,则fail[v]=trie[fail[u]][x]。
- 如果trie[fail[u][x]] != 0,则找fail[fail[u]][x],直到成功或已经找到根节点。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
const int INF = 0x7f7f7f7f;
const int N = 1e6 + 10;
const int M = 50 * 1e4;
const int Z = 26;
int trie[M][Z], tot, fail[M], ed[M];
int newnode()
{
memset(trie[tot], -1, sizeof(trie[tot]));
ed[tot] = 0;
return tot++;
}
void init()
{
tot = 0;
newnode();
}
void insert(char s[])
{
int len = strlen(s);
int p = 0;
for (int i = 0; i < len; i++)
{
int c = s[i] - 'a';
if (trie[p][c] == -1) trie[p][c] = newnode();
p = trie[p][c];
}
ed[p]++;
}
void build()
{
queue<int> q;
fail[0] = 0;
for (int i = 0; i < Z; i++)
{
if (trie[0][i] == -1) trie[0][i] = 0;
else fail[trie[0][i]] = 0, q.push(trie[0][i]);
}
while (!q.empty())
{
int p = q.front(); q.pop();
for (int i = 0; i < Z; i++)
{
if (trie[p][i] == -1) trie[p][i] = trie[fail[p]][i];
else fail[trie[p][i]] = trie[fail[p]][i], q.push(trie[p][i]);
}
}
}
int query(char s[])
{
int len = strlen(s);
int ans = 0, p = 0;
for (int i = 0; i < len; i++)
{
p = trie[p][s[i] - 'a'];
for (int t = p; t; t = fail[t]) ans += ed[t], ed[t] = 0;
}
return ans;
}
char s[N];
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
init();
int n;
scanf("%d", &n);
while (n--)
{
scanf("%s", s);
insert(s);
}
build();
scanf("%s", &s);
printf("%d\n", query(s));
}
return 0;
}