首页 > 其他分享 >后缀数组小结

后缀数组小结

时间:2022-10-02 09:22:13浏览次数:45  
标签:后缀 关键字 int 数组 rm sa 排序 小结

后缀数组小结

借用了一下别人的 \(\rm blog\)。

简介

介绍一下基本数组。

我们把原串 \(\rm A\) 的所有后缀按字典序从小到大排个序,

则排名为 \(\rm i\) 的字符串是以第 \(\rm sa_i\) 个字符为起点的,且以第 \(\rm i\) 个字符为起点的后缀的排名是 \(\rm rk_i\)。

所以有 \(\rm sa_{rk_i}=i\)。

得到 \(\rm sa,rk\) 的方法是基数排序 + 倍增。

基数排序的思想就是按从低位到高位将数排序,可以做到 \(\rm O(n)\)。

解释一下代码:

假设原串是这样的:

m=122;
for(int i=1;i<=n;++i) ++cnt[x[i]=A[i]];
for(int i=2;i<=m;++i) cnt[i]+=cnt[i-1];
for(int i=n;i;--i) sa[cnt[x[i]]--]=i;

这里 \(\rm cnt\) 是一个桶,用来记录每个字符出现的次数,\(\rm m\) 表示桶中数最大值是多少 (因为只有大小写英文字母或数字所以只需要 \(\rm 122\))。

这个代码也很好理解,就是对单个字符 (第一关键字)的排序。

排完序后 \(\rm sa\) 数组长这样:

for(int k=1;k<=n;k*=2){

这表示倍增。

int t=0;
for(int i=n-k+1;i<=n;++i) y[++t]=i;
for(int i=1;i<=n;++i)
	if(sa[i]>k) y[++t]=sa[i]-k;

这表示对第二关键字的排序。

for(int i=1;i<=m;++i) cnt[i]=0;
for(int i=1;i<=n;++i) ++cnt[x[i]];
for(int i=2;i<=m;++i) cnt[i]+=cnt[i-1];
for(int i=n;i;--i) sa[cnt[x[y[i]]]--]=y[i],y[i]=0;

这里就直接结合两关键字的排序搞出了一个总排序。

\(\rm x\) 里面存了上次关键字的排序,在本次排序中即是第一关键字的排序,\(\rm x\) 的值排序等于第一关键字排序,这里的计数排序做的是对的。那么第二关键字呢?

前面对第二关键字进行了排序,在这里 \(\rm x_{y_i}\) 就是根据第二关键字的顺序重新改变了第一关键字的顺序,也就是说在本次计数排序中,出现先后次序排序等于第二关键字大小排序。

换句话说,我们先单独对第二关键字排序,根据这个顺序改变第一关键字的顺序,由于在计数排序时首先按照第一关键字的值排序,而第一关键字的值没有改变所以首先还是根据第一关键字排序,改变的是第一关键字相同的时候,出现在前面的第二关键字排在前面。

然后 \(\rm sa\) 长这样:

swap(x,y);
x[sa[1]]=1,t=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])?t:++t;
if(t==n) break;
m=t;

这里我们就重新搞好了数组,可以继续倍增搞。

完整过程图片如下:




完整代码如下:

inline void get_sa(){
	m=122;
	for(int i=1;i<=n;++i) ++cnt[x[i]=A[i]];
	for(int i=2;i<=m;++i) cnt[i]+=cnt[i-1];
	for(int i=n;i;--i) sa[cnt[x[i]]--]=i;
	for(int k=1;k<=n;k*=2){
		int t=0;
		for(int i=n-k+1;i<=n;++i) y[++t]=i;
		for(int i=1;i<=n;++i)
			if(sa[i]>k)
				y[++t]=sa[i]-k;
		for(int i=1;i<=m;++i) cnt[i]=0;
		for(int i=1;i<=n;++i) ++cnt[x[i]];
		for(int i=2;i<=m;++i) cnt[i]+=cnt[i-1];
		for(int i=n;i;--i) sa[cnt[x[y[i]]]--]=y[i],y[i]=0;
		swap(x,y);
		x[sa[1]]=1,t=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])?t:++t;
		if(t==n) break;
		m=t;
	}
}

标签:后缀,关键字,int,数组,rm,sa,排序,小结
From: https://www.cnblogs.com/into-qwq/p/16748251.html

相关文章

  • 代码随想录 哈希表理论基础,有效的字母异位词(LeetCode 242),两个数组的交集 (LeetCode
    哈希表理论基础哈希表是根据关键码的值而直接进行访问的数据结构。哈希碰撞拉链法拉链法就是要选择适当的哈希表的大小,这样既不会因为数组空值而浪费大量内存,也不会......
  • 前端中数组的方法之 --- Array.prototype.reduce()
    参数:reduce((previousValue,currentValue,currentIndex,array)=>{/*…*/},initialValue)回调函数:previousValue:上一次调用 callbackFn 时的返回值。在第......
  • 数组操作の旋转二维数组
    一、题目描述:​​旋转图像​​给定一个n × n的二维矩阵 matrix表示一个图像。请你将图像顺时针旋转90度。你必须在原地旋转图像,这意味着你需要直接修改输入的二......
  • 机试题 三数之和变形-三数和赛高数组01
    数组a对任意的i、j、k都能找到l,使得ai+aj+ak=al,那么这个数组就是被称为三数和赛高数组,特别的i、j、k需要满足(1<=i<j<k<=n,1<=l<=n)。输入:首先t(1......
  • 机试题 三数之和变形-三数和赛高数组02 存在
    数组a存在i、j、k能找到l,使得ai+aj+ak=al,那么这个数组就是被称为三数和赛高数组,特别的i、j、k需要满足(1<=i<j<k<=n,1<=l<=n)。输入:首先t(1<=......
  • 类与对象的小结
    类和对象的初始化问题初始值public/private数据类型变量名=初始值 初始化块{初始化内容}/*非静态初始化块:作用:给对象进行初始化。对象一建立就运行,且优先于构造......
  • 稀疏数组
    稀疏数组packagearray;importjava.util.Arrays;publicclassSparse{publicstaticvoidmain(String[]args){//创建二维数组System.out.p......
  • Java 数组
    1.数组的定义数组是相同类型数据的有序集合。数字描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。其中,每一个数据称作数组元素,每一个数组元素可以通过......
  • 树状数组+dfs
    P3605[USACO17JAN]PromotionCountingP-洛谷|计算机科学教育新生态(luogu.com.cn)这是一棵树,首先想到了dfs,但是数据范围大,所以不能单纯用dfs想到每个结点只跟他的......
  • java数组
    java数组数组概述  数组声明创建      for语句快速语句:数组.length.for   数组使用内存分析    数组打印快捷键数组名.for......