广义后缀自动机

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;
const int max_n = 1e6 + 5;

char s[max_n];

struct Trie {
    int ch[26]{}, fth, pos;

    Trie() {
        for (int &i:ch)i = 0;
        fth = 0, pos = 0;
    }
} trie[max_n];//NOLINT
int tot_trie = 1;

struct Suffix_Automaton {
    int len, fth;
    int ch[26];

    Suffix_Automaton() {//NOLINT
        len = fth = 0;
        for (int &i :ch)i = 0;
    }
} sam[max_n * 2];//NOLINT
int tot_sam = 1;

inline void Build(int c, int t) {
    int p = trie[trie[t].fth].pos, np = trie[t].pos = ++tot_sam;
    sam[np].len = sam[p].len + 1;
    for (; p && !sam[p].ch[c]; p = sam[p].fth)sam[p].ch[c] = np;
    if (!p) {
        sam[np].fth = 1;
        return;
    }
    int q = sam[p].ch[c];
    if (sam[q].len == sam[p].len + 1) {
        sam[np].fth = q;
        return;
    }
    int nq = ++tot_sam;
    sam[nq] = sam[q];
    sam[nq].len = sam[p].len + 1;
    sam[q].fth = sam[np].fth = nq;
    for (; p && sam[p].ch[c] == q; p = sam[p].fth)sam[p].ch[c] = nq;
}

struct Queue {
    int p, c;
};

inline void bfs() {
    queue<Queue> q;
    for (int i = 0; i < 26; i++)if (trie[1].ch[i])q.push({trie[1].ch[i], i});
    while (!q.empty()) {
        int p = q.front().p, c = q.front().c;
        q.pop();
        Build(c, p);
        for (int i = 0; i < 26; i++)if (trie[p].ch[i])q.push({trie[p].ch[i], i});
    }
}

signed main() {
//    freopen("in", "r", stdin), freopen("out", "w", stdout);
    int n;
    scanf("%d", &n);
    for (int &i:trie[0].ch)i = 1;
    trie[1].pos = 1;
    for (int i = 1; i <= n; i++) {
        scanf("%s", s + 1);
        int len = strlen(s + 1);
        int pos = 1;
        for (int j = 1; j <= len; j++) {
            if (trie[pos].ch[s[j] - 'a'] == 0) {
                trie[pos].ch[s[j] - 'a'] = ++tot_trie;
                trie[trie[pos].ch[s[j] - 'a']].fth = pos;
            }
            pos = trie[pos].ch[s[j] - 'a'];
        }
    }
    bfs();
    long long ans = 0;
    for (int i = 2; i <= tot_sam; i++)ans += sam[i].len - sam[sam[i].fth].len;
    printf("%lld\n", ans);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值