算法学习:后缀数组(SA)
【参考博客】
https://xminh.github.io/2018/02/27/%E5%90%8E%E7%BC%80%E6%95%B0%E7%BB%84-%E6%9C%80%E8%AF%A6%E7%BB%86(maybe)%E8%AE%B2%E8%A7%A3.html
【定义】
【后缀】从第i位到字符串结尾的子串
【解决问题】
从而解决
...................在字符串中找子串
...................比较子串关系
...................查找不同子串的数目
一般来说都是解决
字符串和子串关系的问题
【算法学习】
后缀数组能够在 nlogn的时间复杂度内求取出以下数组
【注意】明白字符串包含字符的范围
SA [] 储存,第 i 个数字表示的是字典序第 i 大的后缀是以 SA_i 开始的后缀
即 “排第几的是哪个后缀”
rank [] 储存,从第i位开始的后缀的字典序排名是 i
即 “某个后缀排第几个”
对SA的求取,我们可以看作对所有的后缀进行排序
而这个排序显然如果直接莽的话肯定T,所以我们需要另外一种方法
这里使用基数排序+倍增的方法进行优化
将所有的后缀进行排序得到SA,这是我们的目的
基数排序,是对两个关键字的元素进行排序从而达到线性复杂度的方法
显然,在当两个后缀第一个字符相等的情况下,我们不可避免的去用第二个字符进行比较
这里我们需要注意到一个事情
第 i 个后缀的第二个字符是第 i + 1 的第一个字符
那这和直接莽好像没有什么区别?
所以我们就用到了倍增,每次确定后缀的以前 2 ^ k 长度的字符串排序的顺序
然后通过这个,就能够求出 2 ^ ( k + 1 ) 长度的后缀的前缀
通过,第 i 位的和第 i + k 位的,于是就能求出来
因为第一个字符有可能一样,导致有两个后缀排名相等
所以总排名数和后缀长度不同
所以长度为2时同理
而当所有总排名和后缀长度相同时,这个时候就找到了所有
下面是对使用到的各个数组的意义的描述:
c[] 桶,记录第 i 位的元素有多少个
x[] 后缀 i 的第一关键字,所以最开始是等于第 i 位字符
y[] 第二关键字排名第 i 的字符串,第一关键字的位置
【代码】
首先求出所需要的几个数组的初值
//初始化int n = strlen(s+1);int m = 128;//m只需要大于 ascii(‘z')即可//因为第一步并不知道桶的规模for (int i = 1; i <= n; i++)++c[x[i] = s[i]];//计算每一个字符的数量,同样的放在一起for (int i = 2; i <= m; i++)c[i] += c[i - 1];//求前缀和for (int i = n; i >= 1; i--) SA[c[x[i]]--] = i;//从后往前,这样就能求出最开始顺序
倍增的同时进行排序
for (int k = 1; k <= n; k <<= 1) //k是之前提到的,每次枚举的字符串长度 {int num = 0;for (int i = n - k + 1; i <= n; i++) y[++num] = i;//先将最后的几个去掉for (int i = 1; i <= n; i++)if (SA[i] > k)y[++num] = SA[i] - k;//当这个位置的后缀在长度之外时//将这个位置的字符放到第二关键字中for (int i = 1; i <= m; i++)c[i] = 0;//清空桶for (int i = 1; i <= n; i++)c[x[i]]++;//放入第一关键字//第一关键字是已经算好的//第一次是初始化时计算好的//第二次的计算过程在下面for (int i = 2; i <= m; i++)c[i] += c[i - 1];//和初始化一样的求前缀和for (int i = n; i >= 1; i--)SA[c[x[y[i]]]--] = y[i], y[i] = 0;//在保证第一关键字的同时使用第二关键字排序//从后往前,第二关键字靠后的会被先剔除掉//考虑下数组的操作 swap(x, y);//这里是为了保存上一步得到的y,没有太多其他意思x[SA[1]] = 1; num = 1;for (int i = 2; i <= n; i++)x[SA[i]] =(y[SA[i]] == y[SA[i - 1]] && y[SA[i] + k] == y[SA[i - 1] + k]) ?//这里注意回忆SA的意义//比较的是和其排名相近的元素,也是最有可能两个关键字都相同的元素//当字符的第二关键字,前后都相同时//说明没有新的元素,所以不加//如果有,则加 ? num : ++num;if (num == n) break;m = num;}
【模板题】
【luogu P3809】
求一个字符串的SA
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int MAXN = 1000010; char s[MAXN]; int SA[MAXN]; int x[MAXN], c[MAXN], y[MAXN]; void get_SA(char *s) {//初始化int n = strlen(s + 1);int m = 128;for (int i = 1; i <= n; i++)++c[x[i] = s[i]];//计算每一个字符的数量,同样的放在一起for (int i = 2; i <= m; i++)c[i] += c[i - 1];//求前缀和for (int i = n; i >= 1; i--)SA[c[x[i]]--] = i;//从后往前,这样就能求出最开始顺序for (int k = 1; k <= n; k <<= 1){int num = 0;for (int i = n - k + 1; i <= n; i++) y[++num] = i;//把最后几个字符放进去for (int i = 1; i <= n; i++)//按照顺序遍历if (SA[i] > k)//如果长度大于ky[++num] = SA[i] - k;//把第一个放进去for (int i = 1; i <= m; i++)c[i] = 0;for (int i = 1; i <= n; i++)c[x[i]]++;//x[i]是第一关键字for (int i = 2; i <= m; i++)c[i] += c[i - 1];//求前缀和for (int i = n; i >= 1; i--)SA[c[x[y[i]]]--] = y[i], y[i] = 0;swap(x, y);x[SA[1]] = 1; num = 1;for (int i = 2; i <= n; i++)x[SA[i]] = (y[SA[i]] == y[SA[i - 1]] && y[SA[i] + k] == y[SA[i - 1] + k]) ? num : ++num;if (num == n) break;m = num;}return; } int main() {scanf("%s", s + 1);get_SA(s);int n = strlen(s + 1);for (int i = 1; i <= n; i++)printf("%d ", SA[i]);return 0; }
View Code
【扩展】
LCP/height数组的求取
转载于:https://www.cnblogs.com/rentu/p/11331536.html
算法学习:后缀数组(SA)相关推荐
- 试题 算法训练 后缀数组——最长重复子串
资源限制 时间限制:100ms 内存限制:256.0MB 问题描述 给定一个长度为n的数串,求至少出现k 次的最长重复子串的长度,这k 个子串可以重叠.保证有子串出现至少k次. 输入格式 第一行:两个 ...
- 算法笔记——后缀数组
后缀指从某个位置开始到字符串末尾的一个子串,用suffix(i)来表示. 后缀数组(suffix array)指将s的所有后缀从小到大排序后,取其下标i放入数组中,该数组就叫做后缀数组. 表示排名为i ...
- F - Anti-Rhyme Pairs(rmq算法模板)(后缀数组算法模板)
点击打开链接 题目大意:通常押韵的两个词以相同的字符结尾.我们运用这个特性来规定反押韵的概念.反押韵是一对拥有近似开头的单词.一对单词的反押韵的复杂度被定义为两者都以之开头且最长的字符串S的长度.因此 ...
- [SDOI2016] 生成魔咒(后缀数组SA + st表 + set)动态不同子串个数
problem luogu-P4070 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 1,21,21,2 拼凑起来形成一个魔咒串 [1,2][1,2][1,2]. 一个魔咒串 ...
- 后缀数组(SA)备忘
两个让我真正理解代码的资料: 2009集训队论文 网上的经典应用都是从里面抄的,还把解释给去掉了...真事屑 这篇博客 代码注释特别好 桶排换成快排的代码,便于理解算法思想 ,这里面要减去k的原因是 ...
- 数据结构与算法学习:数组
今天我们开始学习最基本,最简单的数据类型:数组:它是一种线性数据结构.具有一组连续的内存空间来存储相同数据类型的结构. 数据越界问题思考: 分析下面的代码,栈是由高到低位增长的,所以,i和数组的数据从 ...
- 算法学习-零子数组,最大连续子数组
题目 对于长度为N的数组A,求连续子数组的和最接近0的值. 如: 数组A:1,-2,3,10,-4,7,2,-5 它是所有子数组中,和最接近0的是哪个? 算法流程 申请比A长1的空间sum[-1,0, ...
- 洛谷P2463 [SDOI2008]Sandy的卡片(后缀数组SA + 差分 + 二分答案)
题目链接:https://www.luogu.org/problem/P2463 [题意] 求出N个串中都出现的相同子串的最长长度,相同子串的定义如题:所有元素加上一个数变成另一个,则这两个串相同,可 ...
- 算法竞赛进阶指南——后缀数组
后缀数组 后缀数组 (SA) 是一种重要的数据结构,通常使用倍增或者DC3算法实现,这超出了我们的讨论范围. 在本题中,我们希望使用快排.Hash与二分实现一个简单的O(nlog2n)的后缀数组求法. ...
- 算法竞赛进阶指南:0x14:后缀数组
原题链接 后缀数组 (SA) 是一种重要的数据结构,通常使用倍增或者 DC3 算法实现,这超出了我们的讨论范围. 在本题中,我们希望使用快排.Hash 与二分实现一个简单的 O(nlog^2n) 的后 ...
最新文章
- 求满足从1加到m的和大于1000的最小m值
- asp.net 安装element ui_Vue组件库系列三:打造属于自己的 UI 库文档(新版本的方案)...
- 【EOlymp - 2908】SumThem All(数位统计,tricks)
- 当Typora开始收费,开源免费的MarkText编辑器火了:一周新增2k+star
- C语言之利用,函数的命名及变量的作用域实现两个数之间的交换。
- 不一样的结果,不一样的人生
- python进阶12并发之八多线程与数据同步
- 阻止滑动事件_重温DOM事件流,捕获、冒泡、useCapture、passive
- C编译中如何向代码中传递一个预定义字串
- 【数字信号调制】基于matlab多进制数字频率调制(4FSK)【含Matlab源码 999期】
- free mobile sex java_Java 8中Stream API的这些奇技淫巧!你都Get到了吗?
- 搞懂Nfc刷卡看这篇就够了
- Rational Team Concert 2
- Android Studio适配Mac M1
- 大学生应该读什么书——一位年轻老师给他的年轻学生的一封回信
- thinkPHP6报错:Failed to listen on 0.0.0.0:8000 (reason: һַȨ“
- 方根法公式_方根的简易算法
- 香港大学韩锴课题组招收CV和深度学习方向全奖博士/博后
- Python游戏开发,Python实现贪吃蛇小游戏与吃豆豆 附带源码
- 脑机接口全球Top20实验室信息与概括(有空更新)
热门文章
- 谷歌AMP和百度MIP,你选哪个?
- 阿里云Ubuntu 14.04 + Nginx + let's encrypt 搭建https访问
- 美国会议员提出“漏洞披露法案” 仍考虑非中立实体授权
- 构建高可靠hadoop集群之0-hadoop用户向导
- AngularJS 2.0 学习记录(一)
- hibernate----N-1(一)
- RotateWorldTest对层动作
- configure - 配置源代码树
- apachectl startssl启动apache自动运行输入密码
- 使用思维导图,优雅的完成自己的代码