AcWing
  • 首页
  • 课程
  • 题库
  • 更多
    • 竞赛
    • 题解
    • 分享
    • 问答
    • 应用
    • 校园
  • 关闭
    历史记录
    清除记录
    猜你想搜
    AcWing热点
  • App
  • 登录/注册

AcWing 2572. 生成魔咒    原题链接    困难

作者: 作者的头像   Conan15 ,  2025-04-14 19:36:14 · 福建 ,  所有人可见 ,  阅读 33


1


后缀数组笔记

为什么 xht 注意力这么惊人啊。

从前往后加入字符,对每个前缀求有多少本质不同子串。
并不难想到后缀数组,但是对每个前缀都求一次肯定是爆表的,因为每次在后面加一个字符改动的 $ht$ 数组是 $O(n)$ 级别。
然而一个很巧妙的做法是把字符串倒序,这样每次在最前面加入一个字符。
这样不会改变答案,而且在最前面加入一个字符相当于插入一个 $ht$!

注意其实本质不同子串数可以表示为 $\sum\limits_{i=1}^{n} n - sa_i + 1 - ht_i$,可以通过此公式算出任意一个前缀的答案。

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n, a[N], b[N], m;

int x[N], y[N], cnt[N], sa[N], rk[N], ht[N];
void init_SA() {
    int v = m;
    for (int i = 0; i <= v; i++) cnt[i] = 0;
    for (int i = 1; i <= n; i++) cnt[x[i] = a[i]]++;
    for (int i = 1; i <= v; i++) cnt[i] += cnt[i - 1];
    for (int i = n; i >= 1; i--) sa[ cnt[x[i]]-- ] = i;
    for (int len = 1; ; len <<= 1) {
        int tot = 0;
        for (int i = n - len + 1; i <= n; i++) y[++tot] = i;
        for (int i = 1; i <= n; i++)
            if (sa[i] > len) y[++tot] = sa[i] - len;

        for (int i = 0; i <= v; i++) cnt[i] = 0;
        for (int i = 1; i <= n; i++) cnt[x[i]]++;
        for (int i = 1; i <= v; i++) cnt[i] += cnt[i - 1];
        for (int i = n; i >= 1; i--) sa[ cnt[x[y[i]]]-- ] = y[i], y[i] = 0;

        swap(x, y), tot = 0;
        for (int i = 1; i <= n; i++) {
            if (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + len] == y[sa[i - 1] + len]) x[sa[i]] = tot;
            else x[sa[i]] = ++tot;
        }
        v = tot;
        if (v == n) break;
    }
}

void init_height() {
    for (int i = 1; i <= n; i++) rk[sa[i]] = i;
    for (int i = 1, j = 0, now = 0; i <= n; i++) {
        if (rk[i] == 1) { ht[rk[i]] = 0; continue; }
        j = sa[rk[i] - 1], now = max(now - 1, 0);
        while (i + now <= n && j + now <= n && a[i + now] == a[j + now]) now++;
        ht[rk[i]] = now;
    }
}

long long ans = 0;
set<int> s;

int lg[N], mn[N][19];
void init_ST() {
    lg[1] = 0; for (int i = 2; i <= n; i++) lg[i] = lg[i >> 1] + 1;
    for (int i = 1; i <= n; i++) mn[i][0] = ht[i];
    for (int i = 1; i <= 18; i++)
        for (int j = 1; j + (1 << i) - 1 <= n; j++)
            mn[j][i] = min(mn[j][i - 1], mn[j + (1 << i - 1)][i - 1]);
}
int query(int l, int r) {
    int len = lg[r - l + 1];
    return min(mn[l][len], mn[r - (1 << len) + 1][len]);
}
int LCP(int a, int b) { return query(a + 1, b); }

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]), b[i] = a[i];
    sort(b + 1, b + 1 + n), m = unique(b + 1, b + 1 + n) - b - 1;
    for (int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + 1 + m, a[i]) - b;
    reverse(a + 1, a + 1 + n);
    init_SA(), init_height();
    init_ST();

    for (int i = n; i >= 1; i--) {
        s.insert(rk[i]);
        auto t1 = s.find(rk[i]), t2 = s.find(rk[i]); t2++;
        bool f = 1;
        if (t1 != s.begin()) t1--, ans -= LCP((*t1), rk[i]);
        else f = 0;
        if (t2 != s.end()) ans -= LCP(rk[i], (*t2));
        else f = 0;
        if (f) ans += LCP((*t1), (*t2));
        ans += n - i + 1;
        printf("%lld\n", ans);
    }
    return 0;
}

0 评论

App 内打开
你确定删除吗?
1024
x

© 2018-2025 AcWing 版权所有  |  京ICP备2021015969号-2
用户协议  |  隐私政策  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标 qq图标
请输入绑定的邮箱地址
请输入注册信息