单词「TJOI 2013」(AC自动机)

时间:2021-08-05 21:15:39

传送门

我们正常的建好Trie后求一遍fail。之后对于每一个节点,从它的fail连向它一条单项边。然后从根节点开始dfs。

记sum[i]代表从根到i号节点所代表的的字符串出现的次数,即该点的权值。

设当前的节点为x,他有一个孩子y,则使sum[x] = sum[y]。

记得记录一下每个字符串结尾的节点编号,设第i个字符串结尾的编号为id[i],对于每个字符串i最后输出sum[id[i]]即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector> 
using namespace std;
int trie[5000010][26], fail[5000010], sum[5000010], tot = 1;
int n;
int end_id[210];
char s[5000010];
queue<int> q;
vector<int> vec[5000010];
void solve(int x) {
    for (int i = 0; i < (int)vec[x].size(); i  ) {
        solve(vec[x][i]);
        sum[x]  = sum[vec[x][i]];
    }
}
int main() {
    cin >> n;
    for (int i = 1, p, len; i <= n; i  ) {
        scanf("%s", s   1);
        p = 1;
        len = strlen(s   1);
        for (int j = 1; j <= len; j  ) {
            int k = s[j] - a;
            sum[p]  ;
            if (!trie[p][k]) trie[p][k] =   tot;
            p = trie[p][k];
        }
        sum[p]  ;
        end_id[i] = p;
    }
    for (int i = 0; i < 26; i  ) trie[0][i] = 1;
    fail[1] = 0;
    q.push(1);
    while (!q.empty()) {
        int x = q.front();
        q.pop();
        for (int i = 0; i < 26; i  ) {
            if (!trie[x][i]) {
                trie[x][i] = trie[fail[x]][i];
            } else {
                vec[trie[fail[x]][i]].push_back(trie[x][i]);
                fail[trie[x][i]] = trie[fail[x]][i];
                q.push(trie[x][i]);
            }
        }
    }
    solve(1);
    for (int i = 1; i <= n; i  ) {
        cout << sum[end_id[i]] << "n";
    }
    return 0;
}