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

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

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

题目链接: 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]);
}

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

查看所有标签

猜你喜欢:

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

自流量生活

自流量生活

斯科特·福克斯(Scott Fox) / 王晶晶 / 中信出版社 / 2018-8-1

一位远嫁他国的平凡女孩,陌生的环境、陌生的语言……她不得不从头学起。有写作爱好的她在网络上记录着她学习生活中的小故事。神奇的是,越来越多的人联系她,有人要付钱看新的故事,还有人想把这些故事拍成电视短片。她是怎么做到的? 这本书将告诉你如何利用互联网打造自己的“流量”生活,使你既能获取收入,又能以自己喜欢的方式过一生。在阅读这本书的过程中,你可能会找到自己喜欢的生活方式,了解成功打造自身“流量......一起来看看 《自流量生活》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具