后缀数组
时间:2021-08-05
本文章向大家介绍后缀数组,主要包括后缀数组使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
后缀数组
关键是如何得到sa数组。
采用倍增和基数排序的方法。
因为字符集一般较小, 所以基数排序是首选。
关键是如何倍增处理。 其中的关键思路是二元组递推排序。
二元组排序
考虑多个二元组(x, y), 按照以x为第一关键字, 以y为第二关键字排序。 可以先按y排序, 后按x排序。
倍增过程
字典序的比较是从前向后不断进行的, 因此不断倍增所有后缀前缀的长度。 然后进行排序。
考虑当前要求后缀的前缀长度为k时后缀的排名。 将前缀分为两部分, 构成一个二元组。
先给后部分排序。 如果后部分不存在, 则放在前面。 然后由sa得到剩下的后缀的排序。 同时将编号向前调整。
之后给前缀排序, 排完序后得到sa。
然后将二元组一一映射, 得到新的排序依据。
具体实现关键是x数组和y数组。 x数组存的是上一轮前缀排名的映射值, y为按第二关键字,即当前处理字符串的后缀排序后的后缀编号, 可以直接由sa得到。
具体流程如下:
- 将一个字符排序, x的映射值之间设为字符值即可, 然后用基数排序得到前缀为1的sa排名。
- 将要排序的前缀长度调整扩大二倍, 首先排前缀的后一半。 先把后一半长度不够的放在前面, 前一半的根据sa数组直接排, 将编号大于k的编号减少k, 调整到前缀的起点。
- 之后给前缀的前一半基数排序。 由于后一半已经有序, 只关心前一半即可。
- 根据sa更新x数组, 因为sa内部是有序的, 如果sa[i]和sa[i-1]的前半部分后缀和后半部分后缀相同, 排名设置也相同, 否则排名加一。
- 判断排名是否到了n, 如果没有说明有前缀相同的, 便继续第2步。
具体流程便是倍增长度, 排后半部分, 排前半部分, 更新排序依据, 判断是否结束。
更多细节见代码注释。
代码来自《算法竞赛入门经典——训练指南》, 建议看着书上的图阅读代码, 代码注释中会以该图为例详细解释。
void build_sa(int m) {
//PS:
//过程是不断对所有后缀的前缀排序, 且将后缀分为长度相等的前缀和后缀, 所以下面统一简称这两部分为前缀与后缀。
//x数组为已处理部分的排序映射。即书中最顶上那一组数。不过是从0开始。
//y数组为按照第二关键字,即按前缀的后半部分排序后的后缀编号。 初始可视为 y[i] = i;
int i, *x = t, *y = t2;
//首先处理前缀为1, c数组用于为基数排序。
for(i = 0; i < m; i++) c[i] = 0;
for(i = 0; i < n; i++) c[x[i] = s[i]]++;//这里无论字符集是否确定,都这么写就可以,不过m要足够大。 后面会统一映射到0-n。
for(i = 1; i < m; i++) c[i] += c[i-1];
for(i = n-1; i >= 0; i--) sa[--c[x[i]]] = i;
//可以将注释的代码取消注释,对照书中的图详细观察。 不过x书中不会和书中一样,因为从0开始编号。 不过对结果和过程理解不会影响。
// puts("0\nsa: ");
// for(i = 0; i < n; i++) printf("%d ", sa[i]);
// puts("");
for(int k = 1; k <= n; k <<= 1) {
int p = 0;
// printf("\n%d\n", k);
// printf("x: ");
// for(i = 0; i < n; i++) printf("%d ", x[i]);
//按照第二关键字,即后缀排序。
//后面长度不够的放在前面,也就是所谓的“补0”。
for(i = n-k; i < n; i++) y[p++] = i;
//在上一轮已经处理了排名, 直接拿来用,不过当前排序的是后缀, 因此编号要向前调整k。
for(i = 0; i < n; i++) if(sa[i] >= k) y[p++] = sa[i]-k;
//这里对照书的图例,y[i]已经按照后半部分排好序。
// printf("\ny %d:", p);
// for(i = 0; i < p; i++) printf("%d ", y[i]);
// printf("\nx[y[i]]:");
// for(i = 0; i < n; i++) printf("%d ", x[y[i]]);
//给前缀排序,得到最终排名
for(i = 0; i < m; i++) c[i] = 0;
for(i = 0; i < n; i++) c[x[y[i]]]++;
for(i = 0; i < m; i++) c[i] += c[i-1];
for(i = n-1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
// printf("\nsa: ");
// for(i = 0; i < n; i++) printf("%d ", sa[i]);
//需要用x数组和sa数组更新x数组, y数组已经无用, 用来备份x数组。
//排名从0开始,二元组相同排名相同,否则排名增加。 因为sa中已经有序。
swap(x, y);
p = 1; x[sa[0]] = 0;
for(i = 1; i < n; i++) {
x[sa[i]] = y[sa[i-1]] == y[sa[i]] && y[sa[i-1]+k] == y[sa[i]+k] ? p-1: p++;
}
if(p >= n) break;
m = p;
}
}
原文地址:https://www.cnblogs.com/Maktub-blog/p/15102322.html
- Tomcat9源码——编译环境搭建
- 谈谈个人网站的建立(五)—— 小集群的部署
- 跟Google学写代码--Chromium/base--cpu源码学习及应用
- Spring项目路径
- 跟Google学写代码--Chromium/base--stl_util源码学习及应用
- libphonenumber--windows上编译libphonenumber.lib以及使用(C++、VS2015)
- querySelector与querySelectorAll
- 【技术博客】Spark性能优化指南——基础篇
- 移动端兼容系列 HTML与CSS兼容
- 保存到配置文件
- linux学习第六十篇:Linux监控平台介绍,zabbix监控介绍,安装zabbix,忘记Admin密码如何做
- 支持向量机及Python代码实现
- 【技术博客】Android自定义Lint实践
- UC浏览器皮肤的那个坑
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 手摸手教你撸一个微服务框架-关于服务端的处理
- 聊聊claudb的string command
- windows下安装nodejs
- 【Java面试总结】Java集合
- 《JavaScript 模式》读书笔记(8)— DOM和浏览器模式1
- 《JavaScript 模式》读书笔记(8)— DOM和浏览器模式2
- 5000字 | 24张图带你彻底理解21种并发锁
- JavaScript-变量
- Android应用安装卸载监控
- 细数这些年被困扰过的 TS 问题
- 将WordPress插件Elementor标签插入到WordPress模板文件以使用Elementor编辑
- WordPress自定义新建多区域widget小工具调用
- WordPress调用分类目录 及输出当前分类下的二级目录 和分类文章数量显示
- CSS让Li标签溢出后自动换行
- docker里运行docker命令