首页 > 编程语言 >后缀数组C++详解

后缀数组C++详解

时间:2023-08-10 19:33:40浏览次数:28  
标签:子串 后缀 C++ 详解 数组 sa 排序 rk

后缀定义

“后缀i”代表以第i个字符开头的后缀,存储是用i代表字符串s的后缀s[i...n]

后缀数组是什么?

后缀数组(Suffix Array)主要关系到两个数组:sa 和 rk。

其中,sa[i] 表示将所有后缀排序后第 i 小的后缀的编号,也是所说的后缀数组,后文也称编号数组 sa;

rk[i] 表示后缀 i 的排名,是重要的辅助数组,后文也称排名数组 rk。

这两个数组满足性质:sa[rk[i]]=rk[sa[i]]=i。

解释

后缀数组示例:

image

后缀数组怎么求?

O(n^2logn) 做法
我相信这个做法大家还是能自己想到的:将盛有全部后缀字符串的数组进行 sort 排序,由于排序进行 O(n\log n) 次字符串比较,每次字符串比较要 O(n) 次字符比较,所以这个排序是 O(n^2\log n) 的时间复杂度。
O(nlog^2n) 做法
这个做法要用到倍增的思想。

首先对字符串 s 的所有长度为 1 的子串,即每个字符进行排序,得到排序后的编号数组 sa_1 和排名数组 rk_1。

倍增过程:

用两个长度为 1 的子串的排名,即 \(rk_1[i]\) 和 \(rk_1[i+1]\),作为排序的第一第二关键字,就可以对字符串 s 的每个长度为 2 的子串:\(\{s[i\dots \min(i+1, n)]\ |\ i \in [1,\ n]\}\) 进行排序,得到 sa_2 和 rk_2;

之后用两个长度为 2 的子串的排名,即 rk_2[i] 和 rk_2[i+2],作为排序的第一第二关键字,就可以对字符串 s 的每个长度为 4 的子串:\(\{s[i\dots \min(i+3, n)]\ |\ i \in [1,\ n]\}\) 进行排序,得到 sa_4 和 rk_4;

以此倍增,用长度为 w/2 的子串的排名,即 \(rk_{w/2}[i]\) 和 \(rk_{w/2}[i+w/2]\),作为排序的第一第二关键字,就可以对字符串 s 的每个长度为 w 的子串 \(s[i\dots \min(i+w-1,\ n)]\) 进行排序,得到 sa_w 和 rk_w。其中,类似字母序排序规则,当 i+w>n 时,\(rk_w[i+w]\) 视为无穷小;

\(rk_w[i]\) 即是子串 \(s[i\dots i + w - 1]\) 的排名,这样当 w \geqslant n 时,得到的编号数组 sa_w,也就是我们需要的后缀数组。

#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
char s[N];
int n, w, sa[N], rk[N << 1], oldrk[N << 1];
// 为了防止访问 rk[i+w] 导致数组越界,开两倍数组。
// 当然也可以在访问前判断是否越界,但直接开两倍数组方便一些。
int main() {
    int i, p;
    scanf("%s", s + 1);
    n = strlen(s + 1);
    for (i = 1; i <= n; ++i)
        sa[i] = i, rk[i] = s[i];
    for (w = 1; w < n; w <<= 1) {
        sort(sa + 1, sa + n + 1, [](int x, int y) {
            return rk[x] == rk[y] ? rk[x + w] < rk[y + w] : rk[x] < rk[y];
        }); // 这里用到了 lambda
        memcpy(oldrk, rk, sizeof(rk));
        // 由于计算 rk 的时候原来的 rk 会被覆盖,要先复制一份
        for (p = 0, i = 1; i <= n; ++i) {
            if (oldrk[sa[i]] == oldrk[sa[i - 1]] &&
                oldrk[sa[i] + w] == oldrk[sa[i - 1] + w]) {
                rk[sa[i]] = p;
            } else {
                rk[sa[i]] = ++p;
            } // 若两个子串相同,它们对应的 rk 也需要相同,所以要去重
        }
    }
    for (i = 1; i <= n; ++i)
        printf("%d ", sa[i]);
    return 0;
}

标签:子串,后缀,C++,详解,数组,sa,排序,rk
From: https://www.cnblogs.com/ypzmlmf/p/hzsz.html

相关文章

  • 分治算法C++
    1、光荣的梦想题目描述】Prince对他在这片陆上维护的秩序感到满意,于是决定启程离开艾泽拉斯。在他动身之前,Prince决定赋予King_Bette最强大的能量以守护世界、保卫这里的平衡与和谐。在那个时代,平衡是个梦想。因为有很多奇异的物种拥有各种不稳定的能量,平衡瞬间即被打破。KB决定求......
  • 广度优先搜索C++
    1、细胞(1)题目描述一矩形阵列由数字0到9组成,数字1到9代表细胞,细胞的定义为沿细胞数字上下左右还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数。如:阵列4100234500067103456050020456006710000000089有4个细胞。【输入】第一行为矩阵的行n和列m;下面为一个n×m......
  • 递归算法练习C++
    1、逆波兰表达式(1)题目描述逆波兰表达式是一种把运算符前置的算术表达式,例如普通的表达式2+3的逆波兰表示法为+23。逆波兰表达式的优点是运算符之间不必有优先级关系,也不必用括号改变运算次序,例如(2+3)*4的逆波兰表示法为*+234。本题求解逆波兰表达式的值,其中运算符包括......
  • Google C++ 风格指南记录
    最近在看谷歌的C++风格指南发现了一些有意思的知识点,遂记录下1.第六章第二小节介绍了右值引用只在定义移动构造函数与移动赋值操作时使用右值引用.不要使用 std::forward.定义:右值引用是一种只能绑定到临时对象的引用的一种,其语法与传统的引用语法相似.例如, void......
  • RPM包强制安装详解
    RPM包强制安装详解一、强制安装的含义在进行rpm包安装的过程中,有时会遇到依赖关系不完整、版本不兼容等问题,导致安装失败。这时,我们可以使用强制安装的方法,通过跳过依赖检查、版本检查等环节,强制安装该rpm包。二、强制安装的方式强制安装rpm包有两种方式:1、使用--force选项强制......
  • manacher(马拉车)算法C++详解
    马拉车的定义马拉车本质是对中心扩展法(暴力算法)的优化。马拉车是干什么的Manacher算法帮助我们在给定的字符串中找到最长的回文子串。为了简单起见,我们先只处理有奇数个字符的字符串,关于偶数个字符的字符串,在文章最后会给出解法。我们的处理思路和暴力算法基本一致,那就是从左......
  • 详解ConfuserEx的Anti Tamper与Anti Dump
    title:详解ConfuserEx的AntiTamper与AntiDumpdate:2018-08-14updated:2023-04-11lang:zh-CNcategories:-[.NET逆向]tags:-.NET-逆向工程-脱壳-ConfuserEx-反篡改-反转储toc:true文章首发于https://wwh1004.github.io/inside-confuserex-antitamper-......
  • 详解ILProtector并写出脱壳机
    title:详解ILProtector并写出脱壳机date:2018-11-18updated:2023-04-09lang:zh-CNcategories:-[.NET逆向]tags:-.NET-逆向工程-脱壳-ILProtectortoc:true文章首发于https://wwh1004.github.io/inside-ilprotector-and-writing-an-unpacker/ILProtector的......
  • C/C++基础知识点
    C和C++的区别C++是C的超集,C是面向过程化的结构性语言,而C++是面向对象的编程语言C语言更偏向于底层,使用较为灵活,可移植性强,而C++更偏向于上层,可扩展性强,对于大型项目往往使用C++C++在C语言的基础上提出了STL标准模板库,函数模板等特性static关键字的作用隐藏,凡事变量前添加s......
  • c++枚举详细介绍以及具体用法
    C++中的枚举(Enumeration)是一种用于定义命名常量集合的数据类型。枚举可以提高代码的可读性和可维护性,让您可以使用有意义的名称来表示特定的取值,而不必使用原始的数字常量。枚举的基本语法:enumEnumName{Value1,Value2,//...};EnumName是枚举类型的名称......