首页 > 其他分享 >后缀排序

后缀排序

时间:2023-12-07 23:23:55浏览次数:32  
标签:后缀 tp 关键字 gc sa 排序 rk

先挂个代码和博客吧
blog

#include<bits/stdc++.h>
using namespace std;
#define ri register int
#define gc getchar
template<class T>void in(T &x)
{
    x = 0; bool f = 0; char c = gc();
    while (c < '0' || c > '9') {if (c == '-') f = 1; c = gc();}
    while ('0' <= c && c <= '9') {x = (x << 3) + (x << 1) + (c ^ 48); c = gc();}
    if (f) x = -x;
}
#undef gc
#define N 1000010
char s[N];
int n, m;
// 字符串长度,桶的大小
int sa[N], rk[N];
// SA:排名为i的后缀的位置
// rank:位置为i的后缀的排名
int tp[N], tax[N];
// tp:第二关键字的排名为i的后缀的位置,
//    还被用作rank的暂存
// tax:每个排名对应的后缀数量,用作桶
void rsort() { // 基数排序
    for (ri i = 1; i <= m; ++i) tax[i] = 0;
    for (ri i = 1; i <= n; ++i) ++tax[rk[i]];
    // 第i个桶的内容:第一关键字为第i的数量
    // 我们枚举每个位置的第一关键字排名(rk[i]),添加到桶中
    for (ri i = 1; i <= m; ++i) tax[i] += tax[i - 1];
    // 为了计算方便,我们求出前缀和
    // 注意这里是循环到M
    for (ri i = n; i >= 1; --i) sa[tax[rk[tp[i]]]--] = tp[i];
    // 解释一下:因为第一关键字可能对应很多第二关键字
    // 我们要在第一关键字相同的情况下排第二关键字
    // 为了方便,我们倒序枚举所有的第二关键字,这样它一定是所在桶的最后一名
    // 根据我们就求出的前缀和,它就是所有后缀的第tax[rk[tp[i]]]名
    // ((第二关键字排名为i的后缀的位置)的桶编号)的前缀和排名
    // 因为我们取出了一个元素,所以这个桶的size要-1
    // 这样,这个排名的后缀的位置就是tp[i]
}
void ssort() {
    for (ri i = 1; i <= n; ++i) rk[i] = s[i], tp[i] = i;
    // 初始时,每个字符的排名就是ASCII码
    // 第二关键字就随便给个i
    rsort();
    // 计算出长度为1的SA和rk
    for (ri w = 1, p = 0; p < n && w <= n; m = p, w <<= 1) {
        // m = p : 改变桶的大小为排名的数量,当数量为N时,完成区分排名
        // w是要求出的后缀的长度
        p = 0; // p是计数器,记录有多少个后缀的排名不同
        for (ri i = n - w + 1; i <= n; ++i) tp[++p] = i;
        // 对于起始位置在[n-w+1,n]的后缀,它们没有第二关键字,所以他们的第二关键字最小
        for (ri i = 1; i <= n; ++i)
            if (sa[i] > w) tp[++p] = sa[i] - w;
        // IMPORTANT
        // 枚举每一个排名,看看它距离字符串开头是否>w,这样,这个位置就是一个后缀的第二关键字
        // 因为我们要在每一个后缀前接上长度为w的后缀,所以tp[++p] = sa[i] - w;
        // 注意,编号越小表示这个后缀的起始位置越靠前
        // 我们的第二关键字要从小到大,而我们正好是按照从小到大的顺序枚举每一个后缀
        rsort(); // 再来一遍
        swap(rk, tp); // WARN swap rank & tp
        // 我们已经更新了SA,我们还要更新rk,此时tp已经无用,直接备份上一个rk
        rk[sa[1]] = p = 1; // 排名第一的后缀直接加入
        for (ri i = 2; i <= n; ++i)
            rk[sa[i]] = (tp[sa[i]] == tp[sa[i - 1]] && tp[sa[i] + w] == tp[sa[i - 1] + w])
                        ? p : ++p;
        // 这里的条件是指:它的第一关键字和第二关键字是否都和前一个相同,如果是,它和上一个并列
        // 如果不是,它的排名要+1
        // 检查一下p==n,break;(我们已经区分出所有后缀)
    }
}
signed main() {
    scanf("%s", s + 1);
    n = strlen(s + 1);
    m = 127; // 初始桶的大小,因为char最大就是127
    ssort();
    for (ri i = 1; i <= n; ++i) printf("%d ", sa[i]);
    return 0;
}

标签:后缀,tp,关键字,gc,sa,排序,rk
From: https://www.cnblogs.com/123789456ye/p/12352986.html

相关文章

  • 排序 - 选择排序 & 堆排序
    选择排序简单选择排序算法描述n-1次遍历,每次选出一个未排序区域中的最小元素放入已排序区域中的合适位置。算法实现voidSelectSort(SqList&L){for(i=1;i<L.length;i++){k=i;for(j=i+1;j<=L.length;j++)if(L.r[j].ke......
  • 算法【快速排序】
    算法【快速排序】快速排序。选择一个作为比较的元素,这里我们选择首元素,这个元素我叫他‘比较元素’;前后两个指针(其实是索引变量)同时往后和往前进行遍历,开头的指针遇到比‘比较元素’大的元素停下来(空循环体的循环即可实现),末尾的指针往前遍历,遇到比‘比较元素’小的元素停下来;两个......
  • a-table(AntDesign Vue)实现表格行上下拖动排序
    参考链接:https://blog.csdn.net/song_de/article/details/125218350https://blog.csdn.net/m0_61342618/article/details/132556739?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-1-132556739-blog-125218350.235v39pc_releva......
  • 桶排序
    前K个高频元素给你一个整数数组nums和一个整数k,请你返回其中出现频率前k高的元素。你可以按任意顺序返回答案。 示例1:输入:nums=[1,1,1,2,2,3],k=2输出:[1,2]示例2:输入:nums=[1],k=1输出:[1] 提示:1<=nums.length<=105k的取值范围是[1,......
  • Js中 列表里字典排序问题
    我又要给这样的列表,我想把出现"key3"的字典放到列表的后边constlist=[{key1:'value1',key2:'value2'},{key1:'value3',key2:'value4'},{key3:'value5',key2:'value6'},{key4:'......
  • SpringBoot学习系列-YAML(后缀为.yml)配置文件使用
    学习使用: YAML 是一种可读性高,以数据为中心的数据序列化格式。什么是序列化?序列化指的是将自定义的对象或者其他数据进行持久化,从而方便进行传输和存储。一般情况下,能够序列化的数据一定能够通过反序列化恢复。注:序列化的目的之一是方便持久化数据,定义本身和持久化应该没啥......
  • 上机编程字典序排序总结
    1         字典序概念2021-0319上机编程认证的入门级&工作级第二题-可漫游服务区,输出结果要求字符串按照字典序降序排序,本文对各编程语言字典序排序方法做一个总结。题目描述漫游(roaming)是一种移动电话业务,指移动终端离开自己注册登记的服务区,移动到另一服务区(地区或......
  • 【数据结构和算法】排序算法
    使用swap函数来交换列表中的两项的位置1defswap(lyst,i,j):2'''交换列表中两项的位置'''3temp=lyst[i]4lyst[i]=lyst[j]5lyst[j]=temp①选择排序处于列表第一项,先找到最小项的位置,如果该位置不是列表的第一项,算法会交换这两个位置的项,然后......
  • 冒泡排序法(C语言)
    #include<stdio.h>intmain(){ inti,j; intarr[10]={4,1,3,2,5,8,9,7,6,1};//定义一个数组总元素个数为10 for(i=0;i<9;i++){//外层循环循环次数为数组总元素减一 for(j=0;j<9-i;j++){//内层循环为从一个数开始与右邻进行比较并排序,  if(arr[j]>ar......
  • 秦疆的Java课程笔记:58 数组 冒泡排序
    总共有八大排序,其中冒泡排序无疑是较为出名的排序算法之一。冒泡排序的代码相当简单,两层循环,外层冒泡轮数,里层依次比较。当看到嵌套循环,应该立马意识到,这个算法的时间复杂度是\(O(n^2)\)。冒泡排序基本步骤:比较数组中两个相邻元素,如果第一个数比第二个数大,就交换位置......