【AhoCorasickAutoMata】bzoj3172[Tjoi2013]单词

来源:互联网 发布:法兰克编程教学 编辑:程序博客网 时间:2024/05/16 02:08

传送门:点击打开链接

从浙江玩了一圈回来,提起AC自动机,拍的挺顺的,然后就忘记初始化了……

这道题不难思考,感觉比2938那题还容易想

题目要求的是每个单词在文章中出现的次数,这本身就是一个自己匹配自己的过程,所以很容易想到是在AC自动机上的操作

在建Trie时,先给每一个节点打标机,也就是计算作为前缀的直接出现次数

由于失配函数不遗漏且不重复的,递归地表示了每一个后缀从属关系,自然只要沿着失配边的反方向就能够递推得到每个单词的出现次数了

代码如下:

#include <bits/stdc++.h>using namespace std;#define rep(i, a, b) for(int i = (a); i <= (b); i++)#define red(i, a, b) for(int i = (a); i >= (b); i--)#define ll long longconst int maxn = 1100000, maxsize = 26;const int maxN = 300;int n;int pos[maxN];char s[maxn];struct acam{int sz,nn;int ch[maxn][maxsize];int val[maxn], f[maxn], last[maxn], sum[maxn], list[maxn];acam() { sz = 1; memset(ch[0], 0, sizeof(ch[0])); }int idx(char c) { return c-'a'; }int insert(char* s, int v) {int u = 0, n = strlen(s);rep(i, 0, n-1) {int c = idx(s[i]);if (!ch[u][c]) {memset(ch[sz], 0, sizeof(ch[sz]));val[sz] = 0;ch[u][c] = sz++;}u = ch[u][c];sum[u]++;}val[u] = v;return u;}void getfail() {nn = 0;memset(list, 0, sizeof(list));queue<int> q;rep(c, 0, maxsize-1) {int u = ch[0][c];if (u) { f[u] = 0; q.push(u); last[u] = 0; }}while(!q.empty()) {int r = q.front(); q.pop();list[nn++] = r;rep(c, 0, maxsize-1) {int u = ch[r][c];if (!u) continue;q.push(u);int v = f[r];while(v && !ch[v][c]) v = f[v];f[u] = ch[v][c];last[u] = val[f[u]] ? f[u] : last[f[u]];}}red(i, nn-1, 0) sum[f[list[i]]] += sum[list[i]];}}ac;int main() {scanf("%d", &n);rep(i, 1, n) {scanf("%s", s);pos[i] = ac.insert(s, 1);}ac.getfail();rep(i, 1, n) printf("%d\n", ac.sum[pos[i]]);return 0;}


0 0
原创粉丝点击