洛谷 P3809 【模板】后缀排序

基数排序因为每次只有我们所谓的“第一关键字”和“第二关键字”,所以是一次排序复杂度为O(2n),对len进行倍增,所以只要进行log n次排序。所以总复杂度:O(n log n)。
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,m;
int rk[N],rk2[N],sum[N],tp[N],sa[N];
char s[N];

inline void qsort()
{
	for (register int i=0; i<=m; ++i) sum[i]=0;
	for (register int i=1; i<=n; ++i) sum[rk[i]]++;
	//1.我们把之前的位,当做第一关键字,即rk;
	for (register int i=1; i<=m; ++i) sum[i]+=sum[i-1];
	//2.在第一关键字相同情况下,那么就由第二关键字的大小,觉得sa的值了
	//所以这里我们的n要从大到小枚举
	//第一关键字相同,即rk[tp[i]]相同,这时候就需要第二关键字了 
	for (register int i=n; i>=1; --i) sa[sum[rk[tp[i]]]]=tp[i],sum[rk[tp[i]]]--;
	//[[排名为n的第二关键字  的第一关键字,在原串中的起始位置] 开始的后缀的排名] ,前面有多少个后缀
	//那么,它的排名,就是它 前面的后缀数量 
}
//sa[i]:排名为i的后缀  在原串中的起始位置 
//rk[i]:从第i个位置开始的后缀的排名
//tp[i]:排名为i的第二关键字  的第一关键字 ,在原串中的起始位置 
inline void SA()
{
	m=75;
	for (register int i=1; i<=n; ++i) rk[i]=s[i]-'0'+1,tp[i]=i;
	qsort();
	//第一次基数排序,得到的排名,仅仅是比较每个后缀的第1位 
	int p=0;
	for (register int len=1; p<n; m=p,len<<=1) 
	//从第二次基数排序开始,以后的 第i次基数排序,得到的排名,比较的是每个后缀的前2^(i-1)次位 
	{
		p=0;
		for (register int i=n-len+1; i<=n; ++i) tp[++p]=i;
		//对于第二关键字为空的,最先放进去 
		for (register int i=1; i<=n; ++i) if (sa[i]>len) tp[++p]=sa[i]-len;
		//之后,按照之前的排名sa,一个个把第二关键字放进去
		//因为,比如,len=2时,这个后缀的第一位为1,第二位为2,
		//那么,我们之前求出来的第2位开始的,连续长度为1的后缀的排名,
		//就是我们现在要求的第二关键字的排名 
		qsort();
		memcpy(rk2,rk,sizeof(rk2));
		p=1; rk[sa[1]]=p;
		for (register int i=2; i<=n; ++i)
		{
			if (rk2[sa[i]]==rk2[sa[i-1]] && rk2[sa[i]+len]==rk2[sa[i-1]+len]) p=p; else p++;
			rk[sa[i]]=p;
		}
		//如果在当前len长度下,还没能比较某几个后缀的大小,那么就只能将他们的排名赋值为一样
		//然后,继续倍增len的长度 
	}
	for (register int i=1; i<=n; ++i) printf("%d ",sa[i]);
}

int main(){
	scanf("%s",s+1);
	n=strlen(s+1);
	SA();
return 0;	
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值