SA 后缀数组入门 — Luogu P3809 【模板】后缀排序

栏目: 数据库 · 发布时间: 6年前

内容简介:题目链接:后缀数组用于解决各种玄学字符串问题,准确来说,它是一种思想基于后缀数组有很多好玩

题目链接: https://www.luogu.org/problemnew/show/P3809

后缀数组

后缀数组用于解决各种玄学字符串问题,准确来说,它是一种思想

基于后缀数组有很多好玩

毒瘤

的东西

目前已知的求后缀数组的方法有

因为我太菜了,所以我就讲倍增求法

倍增求后缀数组

与其说倍增求后缀数组,倒不如说是 倍增 + 基数 排序 求后缀数组

我们先从归并排序讲起

基数排序

基数排序,用于给形如 $(a, b)$ 的二元组排序($a$ 为第一关键字,$b$ 为第二关键字)

可以做到在近似 $O(n)$ 的复杂度完成

通过灵活的运用基数排序,可以比绝大多数的 排序算法 更加优秀

因此也被用来在快速排序模板中占领效率 rk1

倍增的用处

我们发现如果我们直接暴力求倍增数组,重复的前缀被计算了多次

如果我们一开设长度为 $1$ 字串为一个单位,排完序后设长度为二为一个单位,我们就会发现这个可以基数排序

之后设长度为 $4$ 为一个单位,依然可以基数排序,以此类推,便可以做到后缀排序

这个过程,也就是倍增

倍增 $O(\log(n))$

基数排序 $O(n)$

套起来 $O(n \log(n))$

代码

#include <cstdio>
#include <cstring>

const int N = 1e6 + 1e5;

int len, Max_char;
int sa[N], tp[N], rank[N], rank_cnt[N];
char str[N];
// Max_char 基数排序最大项
// sa[i] 如上文所述
// tp[i] 第二关键字
// rank[ sa[i] ] = i 从第 i 个字母开始的排名
// rand_cnt[i] 统计 rank 的和,基数排序用

// 基数排序
void sort(){
    memset(rank_cnt, 0, sizeof(rank_cnt));
    // 统计 rank_cnt
    for(int i = 1; i <= len; i++)
        rank_cnt[ rank[i] ] ++;
    // 前缀
    for(int i = 1; i <= Max_char; i++)
        rank_cnt[i] += rank_cnt[i - 1];
    // 计算出 sa
    for(int i = len; i > 0; i--)
        sa[ rank_cnt[ rank[ tp[i] ] ] -- ] = tp[i];    
}

void get_sa(){
    Max_char = 'z' - '0' + 2;
    // 长度为 1 的预处理
    for(int i = 1; i <= len; i++){
        rank[i] = str[i] - '0' + 1;
        tp[i] = i;
    }
    sort();
    // 倍增
    for(int l = 1, tmp_cnt; tmp_cnt < len; l <<= 1, Max_char = tmp_cnt){
        tmp_cnt = 0;
        // 处理第二关键字
        for(int i = 1; i <= l; i++)
            tp[ ++ tmp_cnt ] = len - l + i;
        for(int i = 1; i <= len; i++){
            if( sa[i] > l )
                tp[ ++ tmp_cnt ] = sa[i] - l;
        }
        sort();

        memcpy(tp, rank, sizeof(rank));
        rank[ sa[1] ] = tmp_cnt = 1;
        for(int i = 2; i <= len; i++){
            // 判断重复(判断一个二元组)
            rank[ sa[i] ] = ( tp[ sa[i] ] == tp[ sa[i - 1] ] && tp[ sa[i] + l ] == tp[ sa[i - 1] + l ]) ? tmp_cnt: ++ tmp_cnt;
        }
    }
}

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

    get_sa();

    // 输出
    for(int i = 1; i <= len; i++)
        printf("%d ", sa[i]);
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

精益数据分析

精益数据分析

[加] 阿利斯泰尔·克罗尔、[加] 本杰明·尤科维奇 / 韩知白、王鹤达 / 人民邮电出版社 / 2014-12 / 79.00元

本书展示了如何验证自己的设想、找到真正的客户、打造能赚钱的产品,以及提升企业知名度。30多个案例分析,全球100多位知名企业家的真知灼见,为你呈现来之不易、经过实践检验的创业心得和宝贵经验,值得每位创业家和企业家一读。 深入理解精益创业、数据分析基础,和数据驱动的思维模式 如何将六个典型的商业模式应用到各种规模的新企业 找到你的第一关键指标 确定底线,找到出发点 在大......一起来看看 《精益数据分析》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具