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;
}