[LuoguP2408]不同子串个数(后缀自动机)

Posted _CRしち_のblog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[LuoguP2408]不同子串个数(后缀自动机)相关的知识,希望对你有一定的参考价值。

题目传送门

先建好后缀自动机,然后答案就是$\sum_{u \in 状态集合}len[u]-len[link[u]]$。

为什么这样是对的?

每个状态所代表的字符串是没有交集的,所以我们只需求出每个状态有多少个子串。

其实在学习构建SAM的时候我们学过link的一个性质,就是len[link[x]]+1=min_len(x),且一个状态内的字符串按长度从小到大排序是连续的。

通过这个性质很容易得到上面的式子。

#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
struct SAM{
    int ch[26], link, len;
}tr[N];
int lst = 1, tot = 1;
int n;
long long ans;
char s[N];
void insert(int c) {
    int p = lst;
    int np = lst = ++tot;
    tr[np].len = tr[p].len + 1;
    while (p && !tr[p].ch[c]) tr[p].ch[c] = np, p = tr[p].link;
    if (!p) tr[np].link = 1;
    else {
        int q = tr[p].ch[c];
        if (tr[q].len == tr[p].len + 1) tr[np].link = q;
        else {
            int nq = ++tot;
            tr[nq] = tr[q];
            tr[nq].len = tr[p].len + 1;
            tr[q].link = tr[np].link = nq;
            while (p && tr[p].ch[c] == q) tr[p].ch[c] = nq, p = tr[p].link;
        }
    }
}
int main() {
    cin >> n;
    scanf("%s", s + 1);
    for (int i = 1; i <= n; i++) {
        insert(s[i] - a);
    }
    for (int i = 2; i <= tot; i++) {
        ans += tr[i].len - tr[tr[i].link].len;
    }
    cout << ans;
    return 0;
}

以上是关于[LuoguP2408]不同子串个数(后缀自动机)的主要内容,如果未能解决你的问题,请参考以下文章

后缀自动机模板——不同子串个数p2408

「Luogu P2408」不同子串个数

LUOGU P2408 不同子串个数(后缀数组)

P2408 不同子串个数

HDU4622 (查询一段字符串的不同子串个数,后缀自动机)

字符串-后缀树和后缀数组详解