「SDOI2016」生成魔咒

https://loj.ac/problem/2033
题意:这道题是中文题。就是给你一个字符串,询问每一个前缀中有多少个不同子串。
做法:说到不同子串的数量,应该想到的是某种字符串算法。
这道题应该用后缀自动机解决。
首先一个字符串中不同子串的数量就是从空串起点 t 0 t_0 出发有多少条路径。
我们可以利用SAM的树形结构跑一个动态规划 d p [ u ] = 1 + v d p [ v ] dp[u]=1+\sum_{v}dp[v]
就可以了,但不过这道题这样写似乎很难维护。
我们就可以换一种方法维护。
另一种方法是利用后缀自动机的树形结构。每个节点对应的子串数量是 l e n [ i ] l e n [ l i n k [ i ] ] len[i]-len[link[i]] ,对自动机所有节点求和即可。
这道题没加入一个字符就加上这个结点的值,不用管加入过程中 c l o n e clone 的结点,你可以画一个图,发现他对答案的总和并不影响。

#include "bits/stdc++.h"

using namespace std;
typedef long long ll;
const int maxn = 100000 + 10;
const int inf = 0x3f3f3f3f;
struct state {
    int len, link;
//    int next[26];
    unordered_map<int, int> next;
} st[maxn << 1];
int sz, last;
void init() {
    st[0].len = 0;
    st[0].link = -1;
    sz++;
    last = 0;
}
void extend(int c) {
    int cur = sz++;
    st[cur].len = st[last].len + 1;
    int p = last;
    while (p != -1 && !st[p].next[c]) {
        st[p].next[c] = cur;
        p = st[p].link;
    }
    if (p == -1) {
        st[cur].link = 0;
    } else {
        int q = st[p].next[c];
        if (st[p].len + 1 == st[q].len) {
            st[cur].link = q;
        } else {
            int clone = sz++;
            st[clone].len = st[p].len + 1;
//            memcpy(st[clone].next, st[q].next, sizeof(st[q].next));
            st[clone].next = st[q].next;
            st[clone].link = st[q].link;
            while (p != -1 && st[p].next[c] == q) {
                st[p].next[c] = clone;
                p = st[p].link;
            }
            st[q].link = st[cur].link = clone;
        }
    }
    last = cur;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int n, x;
    ll ans = 0;
    cin >> n;
    init();
    for (int i = 1; i <= n; i++) {
        cin >> x;
        extend(x);
        ans += st[last].len - st[st[last].link].len;
        cout << ans << endl;
    }
    return 0;
}

发布了130 篇原创文章 · 获赞 80 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/KXL5180/article/details/103787626