# 【字符串】后缀排序

## Algorithm

1 ababa
2 baba
3 aba
4 ba
5 a


1 ababa
3 aba
5 a
2 baba
4 ba


5 a
1 ababa
3 aba
4 ba
2 baba


$sa$ 数组当前的值为 $\{5,~1,~3,~4,~2\}$，而 $rnk$ 当前的值为 $\{2，~3，~2，~3，~1\}$

5 a
3 aba
1 ababa
4 ba
2 baba


$sa = \{5,~3,~1,~4,~2\}$$rnk = \{3,~5,~2,~4,~1\}$

### Algorithm $1$

#### Code

#include <cstdio>
#include <algorithm>

const int maxn = 2000005;

int n;
char S[maxn];
int rnk[maxn], sa[maxn];
std::pair<int, int> MU[maxn];

bool cmp(const int &a, const int &b);

int main() {
freopen("1.in", "r", stdin);
for (int i = 1; i <= n; ++i) {
rnk[i] = 1;
sa[i] = i;
MU[i].second = S[i];
}
for (int len = 1; len <= n; len <<= 1) {
std::sort(sa + 1, sa + 1 + n, cmp);
for (int i = 1; i <= n; ++i) {
if (MU[sa[i]] == MU[sa[i - 1]]) {
rnk[sa[i]] = rnk[sa[i - 1]];
} else  {
rnk[sa[i]] = i;
}
}
for (int i = 1; i <= n; ++i) {
MU[i].first = rnk[i];
MU[i].second = rnk[i + len];
}
}
for (int i = 1; i <= n; ++i) {
qw(sa[i], ' ', true);
}
putchar('\n');
return 0;
}

auto beg = p;
do *(++p) = IPT::GetChar(); while (((*p >= 'a') && (*p <= 'z')) || ((*p >= '0') && (*p <= '9')) || ((*p >= 'A') && (*p <= 'z')));
*p = 0;
return p - beg - 1;
}

inline bool cmp(const int &a, const int &b) {
if (MU[a] != MU[b]) {
return MU[a] < MU[b];
} else {
return a < b;
}
}


### Algorithm $2$

#### Code

std::vector<int>bk[maxn];

void RadixSort(int *const beg, int *const ed) {
for (auto it = beg; it != ed; ++it) {
bk[MU[*it].second].push_back(*it);
}
auto p = beg;
for (int i = 0; i <= n; ++i) {
for (auto u : bk[i]) {
*(p++) = u;
}
bk[i].clear();
}
for (auto it = beg; it != ed; ++it) {
bk[MU[*it].first].push_back(*it);
}
p = beg;
for (int i = 0; i <= n; ++i) {
for (auto u : bk[i]) {
*(p++) = u;
}
bk[i].clear();
}
}


### Algorithm $3$

（注意我们下方的假设为已经对 $len$ 做好了排序，现在要排序 $2len$

$\forall i \leq len,~tp_i = n - len + 1$，这一部分是对应第二关键字为 $0$ 的部分。

$\forall sa_i > len,~~tp_{++pos} = sa_i - len$。其中 $pos$ 为计数器，初值为 $len$。注意我们是从小到大枚举 $i$ 而不是枚举 $sa_i$

void RadixSort() {
for (int i = 0; i <= m; ++i) tax[i] = 0;
for (int i = 1; i <= n; ++i) ++tax[rnk[i]];
for (int i = 1; i <= m; ++i) tax[i] += tax[i - 1];
for (int i = n; i; --i) sa[ tax[rnk[tp[i]]]-- ] = tp[i];
}


$tax$ 代表桶，首先将桶清零，然后枚举每个字符串，将字符串的排名放入桶内。

#### Code

#include <ctime>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>

int n, m;
char S[maxn];
int rnk[maxn], sa[maxn], tp[maxn], tax[maxn];

bool cmp(const int &a, const int &b);

int main() {
freopen("1.in", "r", stdin);
for (int i = 1; i <= n; ++i) {
rnk[i] = S[i];
tp[i] = i;
}
m = 1000;
for (int len = 1, p = 0; p < n; len <<= 1, m = p) {
p = 0;
for (int i = 1; i <= len; ++i) tp[++p] = n - len + i;
for (int i = 1; i <= n; ++i) if (sa[i] > len) tp[++p] = sa[i] - len;
std::swap(tp, rnk);
rnk[sa[1]] = p = 1;
for (int i = 2; i <= n; ++i) {
rnk[sa[i]] = ((tp[sa[i - 1]] == tp[sa[i]]) && (tp[sa[i - 1] + len] == tp[sa[i] + len])) ? p : ++p;
}
}
for (int i = 1; i <= n; ++i) {
qw(sa[i], ' ', true);
}
putchar('\n');
return 0;
}

auto beg = p;
do *(++p) = IPT::GetChar(); while (((*p >= 'a') && (*p <= 'z')) || ((*p >= '0') && (*p <= '9')) || ((*p >= 'A') && (*p <= 'z')));
*p = 0;
return p - beg - 1;
}


$Algorithm~~3$ 的代码参考于 @自为风月马前卒blog