初学后缀数组

后缀排序

Description

Tim正在自学《数据结构》,他刚刚学会如何比较两个字符串大小。书上是这么说的(和Pascal语言中的比较规则相同,学习过Pascal语言的同学可以跳过这段): 
比较两个不同字符串s1=’p1p2p3…pN’和s2=’q1q2q3…qM’的大小,设N<=M。 
若s1是s2的前缀,则s1<s2。否则设pi<>qi,且i最小;若pi<qi,则s1<s2,否则s1>s2。 
Tim想通过练习熟练运用这个规则,于是打算出许多字符串,并将它们从小到大排序。可是Tim非常懒,随机写出 K个很长的字符串实在是太麻烦了。不过聪明的他想到了一个好办法,他写了一个很长的字符串,自言自语说,“我只要把这个字符串的所有后缀从小到大排序就可以了”。

Input

输入文件suffix.in中仅有一行,且是一个仅包含小写字母的字符串,长度K不超过10^5。

Output

有K行,每行一个数字,第i行的数字Pi表示所有后缀中,第i小的是由原字符串第Pi个字符引导的后缀。

Sample Input

mississippi

Sample Output

11
8
5
2
1
10
9
7
4
6
3

Source

Oibh(后缀数组)

后缀数组:后缀数组SA 是一个一维数组,它保存1..n 的某个排列SA[1],SA[2],……,SA[n],并且保证Suffix(SA[i]) < Suffix(SA[i+1]),1≤i<n。

也就是将S 的n 个后缀从小到大进行排序之后把排好序的后缀的开头位置顺次放入SA 中。

名次数组:名次数组Rank[i]保存的是Suffix(i)在所有后缀中从小到大排列的“名次”。

倍增算法求后缀数组的主要思路是:用倍增的方法对每个字符开始的长度为2k 的子字
符串进行排序,求出排名,即rank 值。k 从0 开始,每次加1,当2k 大于n 以
后,每个字符开始的长度为2k 的子字符串便相当于所有的后缀。并且这些子字
符串都一定已经比较出大小,即rank 值中没有相同的值,那么此时的rank 值就
是最后的结果。每一次排序都利用上次长度为2k-1 的字符串的rank 值,那么长
度为2k 的字符串就可以用两个长度为2k-1 的字符串的排名作为关键字表示,然
后进行基数排序,便得出了长度为2k 的字符串的rank 值。以字符串“aabaaaab”
为例,整个过程如图2 所示。其中x、y 是表示长度为2k 的字符串的两个关键字。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define maxn 100010
using namespace std;
char s[maxn];
int n,m,tot,sum[maxn],t1[maxn],t2[maxn],rank[maxn],SA[maxn];
void get_SA(){
	int *x=t1,*y=t2;
	for (int i=1;i<=n;i++) sum[x[i]=s[i]]++;
	for (int i=1;i<=255;i++) sum[i]+=sum[i-1];
	for (int i=1;i<=n;i++) SA[sum[x[i]]--]=i;
	tot=0;
	for (int len=1;tot<n;len<<=1,m=tot){
		tot=0;
		for (int i=n-len+1;i<=n;i++) y[++tot]=i;
		for (int i=1;i<=n;i++) if (SA[i]>len) y[++tot]=SA[i]-len;
		for (int i=1;i<=m;i++) sum[i]=0;
		for (int i=1;i<=n;i++) sum[x[y[i]]]++;
		for (int i=1;i<=m;i++) sum[i]+=sum[i-1];
		for (int i=n;i>=1;i--) SA[sum[x[y[i]]]--]=y[i];
		swap(x,y);
		x[SA[1]]=tot=1;
		for (int i=2;i<=n;i++){
			if (y[SA[i]]!=y[SA[i-1]]||y[SA[i]+len]!=y[SA[i-1]+len]) tot++;
			x[SA[i]]=tot;
		}
	}
	for (int i=1;i<=n;i++) rank[i]=x[i];
}
int main(){
	scanf("%s",s+1);
	n=strlen(s+1),m=123;
	get_SA();
	for (int i=1;i<=n;i++) printf("%d\n",SA[i]);
	return 0;
}
时间: 2024-10-13 22:57:23

初学后缀数组的相关文章

POJ 2774 Long Long Message(最长公共子串 -初学后缀数组)

后缀数组的两篇神论文: 国家集训队2004论文集 许智磊 算法合集之<后缀数组--处理字符串的有力工具> 很多人的模版都是用论文上的 包括kuangbin的模版:(DA算法) 模版中比较难理解的地方有两点1.按关键词排序 2.把字符串长度增加一位 按关键词排序的意思其实是基数排序中相当把两位数排序时先排个位,再排十位 这里也一样先排后2^k长度的字符串,再排前2^k长度的字符串,最终排成2^(k+1)字符长度的后缀数组sa 把字符串增加一位,是为了让有意义的串的rank从1开始,还有便于后边不

初学后缀数组记录(然而并不是很会。。&amp;&amp;很水。。)

两篇很赞的博文: 后缀数组 最详细讲解 (这一篇代码注释很详细,可以好好研读一番) 知识点 后缀数组 orz后缀数组代码 后缀数组主要实现思想就是倍增法+基数排序,代码难也是因为这两者要同时运用. 如果对基数排序还不懂,可以参考一下这篇博文: 基数排序 今后还是要继续学这个神仙操作,继续加油. 原文地址:https://www.cnblogs.com/acboyty/p/9520959.html

hdu 5030 Rabbit&#39;s String(后缀数组)

题目链接:hdu 5030 Rabbit's String 题目大意:给定k和一个字符串,要求将字符串拆分成k个子串.然后将每个子串中字典序最大的子串选出来,组成一个包含k个字符串的集合,要求这个集合中字典序最大的字符串字典序最小. 解题思路:网赛的时候试图搞了一下这道题,不过水平还是有限啊,后缀数组也是初学,只会切一些水题.赛后看了一下别人的题解,把这题补上了. 首先对整个字符串做后缀数组,除了处理出sa,rank,height数组,还要处理处f数组,f[i]表示说以0~sa[i]开头共有多少

SPOJ 705 Distinct Substrings(后缀数组)

[题目链接] http://www.spoj.com/problems/SUBST1/ [题目大意] 给出一个串,求出不相同的子串的个数. [题解] 对原串做一遍后缀数组,按照后缀的名次进行遍历, 每个后缀对答案的贡献为n-sa[i]+1-h[i], 因为排名相邻的后缀一定是公共前缀最长的, 那么就可以有效地通过LCP去除重复计算的子串. [代码] #include <cstdio> #include <cstring> #include <algorithm> usi

hdu5769--Substring(后缀数组)

题意:求含有某个字母的某个字符串的不同子串的个数 题解:后缀数组,记录每个位置距离需要出现的字母的距离就可以了.因为不太了解后缀模版卡了一会,还是很简单的. 记住sa和height数组都是1-n的下标. //后缀数组 #include <stdio.h> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll;

hdu 3518 Boring counting 后缀数组LCP

题目链接 题意:给定长度为n(n <= 1000)的只含小写字母的字符串,问字符串子串不重叠出现最少两次的不同子串个数; input: aaaa ababcabb aaaaaa # output 2 3 3 思路:套用后缀数组求解出sa数组和height数组,之后枚举后缀的公共前缀长度i,由于不能重叠,所以计数的是相邻height不满足LCP >= i的. 写写对后缀数组倍增算法的理解: 1.如果要sa数组对应的值也是1~n就需要在最后加上一个最小的且不出现的字符'#',里面y[]是利用sa数

【tyvj1860】后缀数组

描述 我们定义一个字符串的后缀suffix(i)表示从s[i]到s[length(s)]这段子串.后缀数组(Suffix array)SA[i]中存放着一个排列,满足suffix(sa[i])<suffix(sa[i+1]) 按照字典序方式比较定义height[i]表示suffix(sa[i])与suffix(sa[i-1])之间的最长公共前缀长度,其中height[1]=0你的任务就是求出SA和height这两个数组.字符串长度<=200000 输入格式 一行,为描述中的字符串(仅会出现小写

BZOJ 3238 AHOI 2013 差异 后缀数组+单调栈

题目大意: 思路:一看各种后缀那就是后缀数组没跑了. 求出sa,height之后就可以乱搞了.对于height数组中的一个值,height[i]来说,这个值能够作为lcp值的作用域只在左边第一个比他小的位置到右边第一个比他小的位置.这个东西很明显可以倍增RMQ+二分/单调栈. 之后就是数学题了 Σlen[Ti] + len[Tj] = (len + 1) * len * (len - 1),之后吧所有求出来的Σ2 * lcp(Ti,Tj)减掉就是答案. 记得答案开long long CODE:

hdu 5030 Rabbit&#39;s String(后缀数组&amp;二分)

Rabbit's String Time Limit: 40000/20000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 288    Accepted Submission(s): 108 Problem Description Long long ago, there lived a lot of rabbits in the forest. One day, the