首页 > 其他分享 >[luoguP3809]后缀排序

[luoguP3809]后缀排序

时间:2024-08-31 15:04:10浏览次数:13  
标签:include 下标 后缀 int sa 排序 luoguP3809

题意

给定一个字符串,要求将它的所有后缀按照字典序排序,并按顺序输出每个后缀第一个字符的下标。

sol

这是后缀数组(Suffix Array, SA)的板子题。
我们定义:
\(s_{i\cdots j}\) 表示 \(s\) 中下标在 \(i\) 到 \(j\) 之间的子串。
\(sa_i\) 表示排名为 \(i\) 的后缀第一个字符的下标;
\(rk_i\) 表示第一个字符下标为 \(i\) 的后缀的排名。
而本题就是计算 \(sa\) 数组。
最朴素的做法即为将所有后缀进行一次排序,时间复杂度 \(O(n^2\log n)\),这显然是远远不足的,我们需要对其优化。
下面介绍一种倍增的方法,可以 \(O(n\log n)\) 地计算出 \(sa\) 数组。
首先根据所有长度为 \(1\) 的子串对 \(s\) 进行排序,即根据每个字符进行排序。当我们对所有长度为 \(w\) 的子串排序之后,我们将每个后缀的长度为 \(2w\) 的前缀分为长度为 \(w\) 的两部分,根据之前得到的信息对这个后缀进行排序,直到所有数都排列完毕。
使用基数排序可以将复杂度优化到 \(O(n\log n)\)。

代码

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 1000005;

char s[N];
int n;
int sa[N], rk[N], oldrk[N], cnt[N], scd[N];

void get_sa(){
    for (int i = 1; i <= n; i ++ ) cnt[rk[i] = s[i]] ++ ;
    for (int i = 1; i <= 128; i ++ ) cnt[i] += cnt[i - 1];
    for (int i = n; i; i -- ) sa[cnt[rk[i]] -- ] = i;
    for (int w = 1, m = 128, p = 0; ; m = p, p = 0, w <<= 1){
        for (int i = n - w + 1; i <= n; i ++ ) scd[ ++ p] = i;
        for (int i = 1; i <= n; i ++ ) 
            if (sa[i] > w) scd[ ++ p] = sa[i] - w;
        memset(cnt, 0, m + 1 << 2);
        memcpy(oldrk, rk, n + 1 << 2);
        for (int i = 1; i <= n; i ++ ) cnt[rk[i]] ++ ;
        for (int i = 1; i <= m; i ++ ) cnt[i] += cnt[i - 1];
        for (int i = n; i; i -- ) sa[cnt[rk[scd[i]]] -- ] = scd[i];

        p = 0;
        for (int i = 1; i <= n; i ++ ) rk[sa[i]] = (oldrk[sa[i - 1]] == oldrk[sa[i]] && oldrk[sa[i - 1] + w] == oldrk[sa[i] + w]) ? p : ++ p;
        if (p >= n) return ;
    }
}

int main(){
    scanf("%s", s + 1);
    n = strlen(s + 1);

    get_sa();

    for (int i = 1; i <= n; i ++ ) printf("%d ", sa[i]);

    return 0;
}

标签:include,下标,后缀,int,sa,排序,luoguP3809
From: https://www.cnblogs.com/XiaoJuRuoUP/p/-/luoguP3809

相关文章

  • 【数据结构】排序算法篇一
    【数据结构】排序算法篇一1.插入排序(1)基本思想:(2)动态图解:(3)具体步骤:(4)代码实现:(5)特性总结:2.希尔排序(缩小增量排序)(1)基本思想:(2)静态图解:(3)具体步骤:(4)代码实现:(5)特性总结:3.堆排序(1)基本思想:(2)具体步骤:(3)代码实现:(4)特性总结:4.选择排序(1)基本思想:(2)动态图解:(3)具体步骤:(4)代码实现:(5)特......
  • Java 中的各种排序:详细教程
    1.前言本文通过许多代码示例逐步解释如何对Java中原始数据类型(int、long、double等)和任何类的对象进行排序。具体来说,本文主要回答了以下问题:如何对Java中原始数据类型的数组进行排序?如何对Java中的对象数组和列表进行排序?如何在Java中并行排序?JDK内部使用哪些排......
  • C#学习笔记本--第三篇(排序算法之归并排序)
    一、基本原理://归并=递归+合并//数组分左右 //左右元素相比较//满足条件放入新数组//一侧用完放对面//递归不停分分完在排序//排序结束往上走边走边合并//走到头顶出结果//归并排序分为两部分//1.基本排序规则//2.递归平分数组//递归平分数组://不停地分割......
  • 快速排序python实现
    defquick_sort(arr,left,right):origin_left=leftorigin_right=rightpivot_data=arr[left]#枢轴上的值(基准值),就是开始用来比较的值,一般是随机选择一个位置,这儿选择最左边的值#blank_pos=left#最左边的值已经复制到pivot中了,所以这块......
  • 原神角色数据列表:数据更新到5.0版本,更换品质排序背景颜色,列表可以显示攻略
    <!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0"><title>原神角色数据列表</title>......
  • sort排序
    sort排序任务一:选第k大的元素#include<bits/stdc++.h>//任务:选第k大的元素usingnamespacestd;intmain(){vector<int>a={1,9,2,6,7,8};//方法一:从小到大排序(默认)sort(a.begin(),a.end());intk;cin>>k;cout<<a[a.size()-k]<<e......
  • Java算法之Gnome 排序
    简介Gnome排序,又称为双向插入排序或鸡尾酒排序,是一种改进的插入排序算法。它在每次迭代中不仅将最小的元素移动到前面,同时也将最大的元素移动到后面。这种排序算法在每次迭代中同时向两个方向进行移动,因此得名。算法步骤从数组的两端开始,向中间进行扫描。如果左侧元素大于......
  • Java算法之基数排序(Radix Sort)
    简介基数排序是一种非比较型整数排序算法,其原理是按照低位先排序,然后收集,再按照高位排序,再收集,依次类推,直到最高位。这种方法可以视为对每个位上的数字进行稳定的排序。算法步骤确定最大数的位数。对每一位进行排序:从最低位开始,使用稳定的排序算法(如计数排序)对当前位进......
  • ElasticSearch学习笔记(三)RestClient操作文档、DSL查询文档、搜索结果排序
    文章目录前言5RestClient操作文档5.4删除文档5.4修改文档5.5批量导入文档6DSL查询文档6.1准备工作6.2全文检索查询6.3精准查询6.4地理坐标查询6.5复合查询6.5.1相关性算分6.5.2布尔查询7搜索结果处理7.1排序7.1.1普通字段排序7.1.2地理坐标排序......
  • Java中的数组用法(复制、替换、查找与排序)
    在Java编程中,数组是一种基础且强大的数据结构,用于存储一组相同类型的元素。本文将深入探讨数组在Java中的用法,并展示如何进行数组的复制与替换、查找以及排序。(这些了解与学习只需要一个IDEA就可以进行练习了 )##数组的声明与初始化在Java中,数组的声明和初始化非常直观。以......