码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2020-06-13 23:03:48      阅读:66      评论:0      收藏:0      [点我收藏+]

标签:i++   需求   个数   out   link   字符   pac   target   后缀   

题目传送门

先建好后缀自动机,然后答案就是$\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]不同子串个数(后缀自动机)

标签:i++   需求   个数   out   link   字符   pac   target   后缀   

原文地址:https://www.cnblogs.com/zcr-blog/p/13121907.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!