后缀数组求法(搬运)

请参看@自为风月马前卒的博客,本博客只是为了帮助我自己理解。

原作者:自为风月马前卒 大佬

个人博客

出处

后缀数组的定义:以 a a b a a a a b aabaaaab aabaaaab为例:
它的后缀数组有:
1. a a b a a a a b aabaaaab aabaaaab;
2. a b a a a a b abaaaab abaaaab;
3. b a a a a b baaaab baaaab;
4. a a a a b aaaab aaaab;
5. a a a b aaab aaab;
6. a a b aab aab;
7. a b ab ab;
8. b b b
排名应如下:
rank1: a a a a b aaaab aaaab;
rank2: a a b aab aab;
rank3: b b b;
rank4: a a b a a a a b aabaaaab aabaaaab;
rank5: a b a a a a b abaaaab abaaaab;
rank6: b a a a a b baaaab baaaab;
rank7: a a a b aaab aaab;
rank8: a b ab ab;
(PS:哪位大佬能告诉我为什么这样排啊 Q A Q QAQ QAQ
求法:
1.更新第一关键字( r a n k rank rank, t p tp tp);
2.由第一关键字更新 s a sa sa;
3.从 1 1 1枚举 w w w,更新第二关键字: t p tp tp;
4.由第二关键字更新 s a sa sa;
5.更新 r a n k rank rank r a n k [ s a [ i ] ] = i rank [ sa [ i ] ]=i rank[sa[i]]=i);
6.如果计数器 p = = l e n − 1 p==len-1 p==len1输出 s a sa sa,否则倍增 w w w,重复第3~5步;

Code

//洛谷R19784739
#include<cstdio>
#include<cstring>
#define maxn 1000005
using namespace std;

int len,mx;
int sa[maxn],tp[maxn],rank[maxn],t[maxn];//t[i]是桶
char s[maxn];

void qsort()
{
	for(int i=0;i<=mx;++i)t[i]=0;
	for(int i=1;i<=len;++i)t[rank[i]]++;//桶排序
	for(int i=1;i<=mx;++i)t[i]+=t[i-1];//前缀和
	for(int i=len;i>=1;--i)sa[t[rank[tp[i]]]--]=tp[i];//求当前的后缀数组排名
}

void suffixsort()
{
	mx=75;
	for(int i=1;i<=len;++i)
	{
		rank[i]=s[i]-'0'+1;
		tp[i]=i;
	}//rank[i]是s[i]的ASKII排名,tp[i]是原始排名
	qsort();
	for(int w=1,p=0;p<=len-1;mx=p,w<<=1)
	{
		//w:当前倍增的长度,w = x表示已经求出了长度为x的后缀的排名,现在要更新长度为2x的后缀的排名
    	//p表示不同的后缀的个数,很显然原字符串的后缀都是不同的,因此p = N时可以退出循环
    	p=0;//计数器
    	for(int i=1;i<=w;++i)tp[++p]=len-w+i;
    	for(int i=1;i<=len;++i)
    	{
			if(sa[i]>w)
			tp[++p]=sa[i]-w;
		}
		qsort();//用第二轮关键字求出sa
		memcpy(tp,rank,sizeof(rank));//tp已经没用了
		rank[sa[1]]=p=1;//更新
		for(int i=2;i<=len;++i)
		{
			rank[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p;
		}//看不懂QAQ
	}//倍增求后缀数组
	for(int i=1;i<=len;++i)
	printf("%d ",sa[i]);
}//后缀数组排序

int main()
{
	scanf("%s",s+1);
	len=strlen(s+1);
	suffixsort();
	return 0;
}

我不生产代码,我只是代码的搬运工。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值