P5353 【模板】树上后缀排序

题目地址:【模板】树上后缀排序

我们尝试把普通 SA 改成树上 SA,所以先把普通 SA 贴上来。

namespace SA {
    int sa[N], rk[N], tp[N], tx[N];

    inline void tsort() {
        for (int i = 1; i <= m; i++) tx[i] = 0;
        for (int i = 1; i <= n; i++) ++tx[rk[i]];
        for (int i = 1; i <= m; i++) tx[i] += tx[i-1];
        for (int i = n; i; i--) sa[tx[rk[tp[i]]]--] = tp[i];
    }

    inline bool pd(int i, int w) {
        return tp[sa[i-1]] == tp[sa[i]] && tp[sa[i-1]+w] == tp[sa[i]+w];
    }

    inline void main() {
        for (int i = 1; i <= n; i++) rk[i] = s[i] - 'a' + 1, tp[i] = i;
        tsort();
        for (int w = 1, p = 0; p < n; w <<= 1, m = p) {
            p = 0;
            for (int i = 1; i <= w; i++) tp[++p] = n - w + i;
            for (int i = 1; i <= n; i++) if (sa[i] > w) tp[++p] = sa[i] - w;
            tsort(), swap(rk, tp), rk[sa[1]] = p = 1;
            for (int i = 2; i <= n; i++) rk[sa[i]] = pd(i, w) ? p : ++p;
        }
    }
}

想要把普通 SA 改成树上 SA,仔细观察上面的代码可以发现:

  1. \(tsort\) 函数肯定是不用改的;
  2. \(pd\) 函数可以用树上倍增实现;
  3. \(main\) 函数似乎也很好改?

于是开始改改改,突然发现有个问题:由于序列上每个后缀长度都不一样,所以不可能出现完全相同的字符串,可是在树上是可能出现这种情况的。

然后就没办法了么?

办法肯定是有的要不然这道题是咋出出来的

我们来思考一下,在倍增的每一轮,基数排序究竟要达到什么目的?

对于普通 SA,在倍增的每一轮,假设已经对所有长度为 \(x\) 的串排好序了。“第一关键字”和“第二关键字”代表了两个首尾相接的长度为 \(x\) 的串,称为“主串”和“次串”。基数排序通过 \(O(n)\) 的时间,将每一对“主串”和“次串”合并成一个长度为 \(2x\) 的新串并保持合并后有序

这样可以保证 \(O(\log n)\) 次后,所有后缀呈有序状态。

对于树上 SA,也是同样的。只不过,除了“主串”作为第一关键字,“次串”作为第二关键字以外,为了保证合并后的有序性,我们还要额外将上一轮的有序状态作为第三关键字。同时第二关键字也不能简单地用原先的 \(rk\) 数组构造(因为 \(rk\) 数组会出现相同的排名),而要额外使用没有重复的数组(下面代码中的 \(rkk\) 数组)构造。

总而言之,我们需要使用两次基数排序来达到目的,具体实现请参考代码因为这说得实在是太抽象了

namespace SA {
    int sa[N], rk[N], rkk[N], tp[N], rk2[N], tx[N];

    inline void tsort(int *sa, int *rk, int *tp, int m) {
        for (int i = 0; i <= m; i++) tx[i] = 0;
        for (int i = 1; i <= n; i++) ++tx[rk[i]];
        for (int i = 1; i <= m; i++) tx[i] += tx[i-1];
        for (int i = n; i; i--) sa[tx[rk[tp[i]]]--] = tp[i];
    }

    inline bool pd(int i, int t) {
        return tp[sa[i-1]] == tp[sa[i]] && tp[f[t][sa[i-1]]] == tp[f[t][sa[i]]];
    }

    inline void main() {
        int p = 0;
        for (int i = 1; i <= n; i++) a[i] = s[i] - 'a' + 1, tp[i] = i;
        tsort(sa, a, tp, n);
        rk[sa[1]] = rkk[sa[1]] = p = 1;
        for (int i = 2; i <= n; i++) {
            rk[sa[i]] = a[sa[i-1]] == a[sa[i]] ? p : ++p;
            rkk[sa[i]] = i;
        }
        for (int w = 1, t = 0; w < n; w <<= 1, ++t) {
            for (int i = 1; i <= n; i++) rk2[i] = rkk[f[t][i]];
            tsort(tp, rk2, sa, n);
            tsort(sa, rk, tp, p);
            swap(rk, tp);
            rk[sa[1]] = rkk[sa[1]] = p = 1;
            for (int i = 2; i <= n; i++) {
                rk[sa[i]] = pd(i, t) ? p : ++p;
                rkk[sa[i]] = i;
            }
        }
    }
}

原文地址:https://www.cnblogs.com/xht37/p/10821322.html

时间: 2024-10-27 18:20:44

P5353 【模板】树上后缀排序的相关文章

luogu P3809 【模板】后缀排序

二次联通门 : luogu P3809 [模板]后缀排序 /* luogu P3809 [模板]后缀排序 后缀数组 sa表示 排名为i的是第几个后缀 求出sa数组后输出即可 */ #include <cstdio> #include <cstring> #define Max 1000008 void read (int &now) { register char word = getchar (); for (now = 0; word < '0' || word

洛谷P3809 【模板】后缀排序

题目背景 这是一道模板题. 题目描述 读入一个长度为 nn 的由大小写英文字母或数字组成的字符串,请把这个字符串的所有非空后缀按字典序从小到大排序,然后按顺序输出后缀的第一个字符在原串中的位置.位置编号为 11 到 nn . 输入输出格式 输入格式: 一行一个长度为 nn 的仅包含大小写英文字母或数字的字符串. 输出格式: 一行,共n个整数,表示答案. 输入输出样例 输入样例#1: 复制 ababa 输出样例#1: 复制 5 3 1 4 2 说明 n <= 10^6n<=106 更新了一下自己

P3809 【模板】后缀排序

\(\color{#0066ff}{题目描述}\) 读入一个长度为 n的由大小写英文字母或数字组成的字符串,请把这个字符串的所有非空后缀按字典序从小到大排序,然后按顺序输出后缀的第一个字符在原串中的位置.位置编号为 1 到 n. \(\color{#0066ff}{输入格式}\) 一行一个长度为 n 的仅包含大小写英文字母或数字的字符串. \(\color{#0066ff}{输出格式}\) 一行,共n个整数,表示答案. \(\color{#0066ff}{输入样例}\) ababa \(\col

[UOJ#35] [UOJ后缀数组模板题] 后缀排序 [后缀数组模板]

后缀数组,解决字符串问题的有利工具,本题代码为倍增SA算法 具体解释详见2009年国家集训队论文 1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstdlib> 5 #include <cstring> 6 #include <cmath> 7 #include <ctime> 8 9 using namespace

uoj35 后缀排序

题目链接:http://uoj.ac/problem/35 这是一道模板题. 读入一个长度为 n 的由小写英文字母组成的字符串,请把这个字符串的所有非空后缀按字典序从小到大排序,然后按顺序输出后缀的第一个字符在原串中的位置.位置编号为 1 到 n. 除此之外为了进一步证明你确实有给后缀排序的超能力,请另外输出 n?1 个整数分别表示排序后相邻后缀的最长公共前缀的长度. 输入格式 一行一个长度为 n 的仅包含小写英文字母的字符串. 输出格式 第一行 n 个整数,第 i 个整数表示排名为 i 的后缀

uoj #35. 后缀排序

这是一道模板题. 读入一个长度为 n n 的由小写英文字母组成的字符串,请把这个字符串的所有非空后缀按字典序从小到大排序,然后按顺序输出后缀的第一个字符在原串中的位置.位置编号为 1 1 到 n n. 除此之外为了进一步证明你确实有给后缀排序的超能力,请另外输出 n?1 n?1 个整数分别表示排序后相邻后缀的最长公共前缀的长度. 输入格式 一行一个长度为 n n 的仅包含小写英文字母的字符串. 输出格式 第一行 n n 个整数,第 i i 个整数表示排名为 i i 的后缀的第一个字符在原串中的位

1500 后缀排序

1500 后缀排序 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 大师 Master 题解 查看运行结果 题目描述 Description 天凯是MIT的新生.Prof. HandsomeG给了他一个长度为n的由小写字母构成的字符串,要求他把该字符串的n个后缀(suffix)从小到大排序. 何谓后缀?假设字符串是S=S1S2……Sn,定义Ti=SiSi+1……Sn.T1, T2, …, Tn就叫做S的n个后缀. 关于字符串大小的比较定义如下(比较规则和PASCAL中的定义完全

codevs1500 后缀排序

题目描述 Description 天凯是MIT的新生.Prof. HandsomeG给了他一个长度为n的由小写字母构成的字符串,要求他把该字符串的n个后缀(suffix)从小到大排序. 何谓后缀?假设字符串是S=S1S2……Sn,定义Ti=SiSi+1……Sn.T1, T2, …, Tn就叫做S的n个后缀. 关于字符串大小的比较定义如下(比较规则和PASCAL中的定义完全相同,熟悉PASCAL的同学可以跳过此段): 若A是B的前缀,则A<B:否则令p满足:A1A2…Ap-1=B1B2…Bp-1,

【Luogu3804】【模板】后缀自动机(后缀自动机)

[Luogu3804][模板]后缀自动机(后缀自动机) 题面 洛谷 题解 一个串的出现次数等于\(right/endpos\)集合的大小 而这个集合的大小等于所有\(parent\)树上儿子的大小 这样子的话,给每个终止位置的\(size\)记为\(1\) 然后按照拓扑序累加,这就是\(right/endpos\)集合的大小 最后对于每个\(size>1\)的节点,\(ans=max(longest*size)\) #include<iostream> #include<cstdi