单词 (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;
}

猜你喜欢

转载自www.cnblogs.com/2aptx4869/p/12692682.html