单词 (AC自动机)
题目描述
某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。
输入描述:
第一个一个整数N,表示有多少个单词,接下来N行每行一个单词。每个单词由小写字母组成,N ≤ 200,单词长度不超过10^6
输出描述:
输出N个整数,第i行的数字表示第i个单词在文章中出现了多少次。
示例1
输入
3
a
aa
aaa
输出
6
3
1
题解
AC自动机概念题,考的对 fail指针的理解,就是建树bfs这里就体现了AC自动机手写队列的好处,省了一次bfs
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
const int M = 26;
const int C = 'a';
struct AC
{
int tr[maxn][M], cnt[maxn], fail[maxn], endpos[maxn];
int q[maxn], tot, head, tail;
void insert(char *s, int id)
{
int p = 0;
for (int i = 0; s[i]; ++i)
{
int ch = s[i] - C;
if (tr[p][ch] == 0) tr[p][ch] = ++tot;
p = tr[p][ch], ++cnt[p];
}
endpos[id] = p;
}
void build()
{
head = 0, tail = -1;
for (int i = 0; i < M; ++i)
if (tr[0][i]) q[++tail] = tr[0][i];
while (head <= tail)
{
int p = q[head++];
for (int i = 0; i < M; ++i)
if (tr[p][i])
fail[tr[p][i]] = tr[fail[p]][i], q[++tail] = tr[p][i];
else tr[p][i] = tr[fail[p]][i];
}
}
}ac;
int n;
char s[maxn];
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%s", s), ac.insert(s, i);
ac.build();
for (int i = ac.tail; i >= 0; --i)//省去了bfs
ac.cnt[ac.fail[ac.q[i]]] += ac.cnt[ac.q[i]];
for (int i = 1; i <= n; ++i)
printf("%d\n", ac.cnt[ac.endpos[i]]);
return 0;
}

浙公网安备 33010602011771号