[Luogu3809] 【模板】后缀排序 [SA]

Link
Luogu - https://www.luogu.org/problemnew/show/P3809


后缀排序是后缀数组的基本功能:将一个长度为 n n n 的字符串所有的后缀在 O ( n ) O(n) O(n) 时间内按照字典序排序。

涉及到一个基数排序……然后我发现很多人都一知半解啊?
SA[Tax[Rk[Tp[i]]]--] = Tp[i]; 这一句是个啥?
逐个分析的话就是,
找到第二关键字排名为 i 的那个后缀 Tp[i]
找到 Tp[i] 的第一关键字的排名 Rk[…]
接着,把所有后缀按照第一关键字+第二关键字排序后,找到第一关键字相同的所有后缀里面,
排最后的那个后缀在所有后缀里的排名 Tax[…]
……然后把这个排名对应的后缀设置为 Tp[i]
接着,把这个排名减一 ???????????????
——实际上,在求前缀和之后, Tax[i] 的意义是:第一关键字排名不大于 i \pmb i iii 的后缀的数量
并且这个意义仅在枚举对应的第一关键字的时候有效!!!
如果你要让它全程保持一个很讲得通的意义的话复杂度就退化啦。
另外,基数排序之前 Tp[] 的意义是第二关键字排第 i i i 小的后缀的位置;
基数排序之后 Tp[] 的意义没有了,不过这个时候 SA[] 是按照第一+第二关键字排序完毕的。
这时候 Rk[] 还没有跟上所以最后要加一步处理。

至于第一关键字和第二关键字是什么呀?
后缀数组的倍增挺巧妙的,用心去感受(x
实际上就是这样嘛。比如说
BANANA$
第一个后缀
BANANA$
它第一次的两个关键字
B A
第二次
BA NA
第三次
BANA NA$
就这么个意思。

排序的时候如
B A N A N A $
2 1 3 1 3 1 4
BA AN NA AN NA A$ $
21 13 31 13 31 14 40
3 1 4 1 4 2 5
BANA ANAN NANA ANA$ NA$ A$ $
34 11 44 12 45 20 50
4 1 5 2 6 3 7
实际上,在这一步就已经排序完成了。
如果没有的话,那还要继续,就变成
BANANA$ ANANA$ NANA$ ANA$ NA$ A$ $
然后一定会结束。


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iostream>
#include<cstring>
using namespace std;
#define R register
const int MAXN = 2e6 + 5;
string S;
int n, m, SA[MAXN], Rk[MAXN], Tp[MAXN], Tax[MAXN];
void Radix_Sort()
{
	fill(Tax, Tax + 1 + m, 0);
	for (R int i = 1; i <= n; ++i) ++Tax[Rk[i]];
	for (R int i = 1; i <= m; ++i) Tax[i] += Tax[i - 1];
	for (R int i = n; i >= 1; --i) SA[Tax[Rk[Tp[i]]]--] = Tp[i];
}
void Suffix_Sort()
{ 
    m = 127;
	for (R int i = 1; i <= n; ++i) Rk[i] = S[i], Tp[i] = i; // initialize
	Radix_Sort(); // First Round Suffix_Sort
	
	for (R int Len = 1, tot = 0; tot < n; m = tot, Len <<= 1)
	{
		// Initialize Tp[]
		tot = 0;
		for (R int i = n - Len + 1; i <= n; ++i) Tp[++tot] = i; // They DONT have second keywords
		for (R int i = 1; i <= n; ++i)
		{
			if (SA[i] > Len) 
			{
				// So it could be a second keyword ...
				Tp[++tot] = SA[i] - Len; // SA[i] is the keyword of (SA[i] - Len)
			}
		}
		
		Radix_Sort(); // Next Round Suffix_Sort
		// Tp[] is useless now.
		
		swap(Rk, Tp); // Copy Rk[] to Tp[]
		// And then We'll update Rk[]
		
		// Descretization
		Rk[SA[1]] = tot = 1;
		for (R int i = 2; i <= n; ++i) 
		{
			if (!(Tp[SA[i]] == Tp[SA[i - 1]] && Tp[SA[i] + Len] == Tp[SA[i - 1] + Len])) ++tot;
			Rk[SA[i]] = tot;
		}
		
	}
}
int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> S;
	n = S.size();
	for (R int i = n; i >= 1; --i) S[i] = S[i-1];
	Suffix_Sort();
	for (R int i = 1; i <= n; ++i) cout << SA[i] << " ";
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值