题目描述
某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。
输入描述:
第一个一个整数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 ", ac.cnt[ac.endpos[i]]); return 0; }