P3809 【模板】后缀排序 · znzryb
## 思路讲解
后缀数组板题,数据还是有点水,我才用的做法是O(nloglog)的(快排排logn次)
数据不止有点水,和loj比也水,测不出来我代码的问题。
## AC代码
[https://loj.ac/s/2237091](https://loj.ac/s/2237091)
这个是字符串哈希+二分的做法(洛谷稍微卡了一下,我也就不用快读什么的过了)
```cpp
#include <iostream>
#include <cstring>
#include <algorithm>
#include <deque>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <unordered_map>
#include <cmath>
#include <bitset>
#include <iterator>
#include <random>
#include <iomanip>
#include <cctype>
#include <array>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
typedef array<ll,3> arr;
const ll MAXN=static_cast<ll>(1e6)+10;
const ll /*mod=static_cast<ll>(1e17)+3,*/base=131;
ll N,T,hashe[MAXN],sa[MAXN],height[MAXN],powBase[MAXN];
string S;
inline ll get_hash(ll a,ll b){
// if(a<b)
// return -1;
// 就是b的哈希值-(a-1)的哈希值取模,对a==0的情况特判了
return hashe[b]-(a>0?hashe[a-1]:0)*powBase[b-a+1];
}
inline bool comp(ll a,ll b){
// 使用二分查找来枚举重叠了几位
ll l=0,r=min(N-a,N-b);
while (l<r) {
ll mid=l+r+1>>1;
if(get_hash(a,a+mid-1)==get_hash(b,b+mid-1)){
l=mid;
}else{
r=mid-1;
}
}
if(a+l<N && b+l<N && S[a+l]!=S[b+l]){
return S[a+l]<S[b+l];
}
// 返回长度小的
return N-a<N-b;
}
inline void get_height(){
for(int i=1;i<N;++i){
ll a=sa[i-1],b=sa[i];
// 使用二分查找来枚举重叠了几位
ll l=0,r=min(N-a,N-b);
while (l<r) {
ll mid=l+r+1>>1;
if(get_hash(a, a+mid-1)==get_hash(b, b+mid-1)){
l=mid;
}else{
r=mid-1;
}
}
height[i]=l;
}
}
inline void init(){
ll val=0;
for(int i=0;i<N;++i){
val=base*val+S[i];
hashe[i]=val;
sa[i]=i;
}
powBase[0]=1;
for(int i=1;i<=N+5;++i){
powBase[i]=powBase[i-1]*base;
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>S;
N=S.size();
init();
sort(sa, sa+N, comp);
comp(67,60);
for(int i=0;i<N;++i)
cout<<sa[i]+1<<' ';
cout<<'\n';
}
/*
ponoiiipoi
*/
```
下面是倍增法
[https://loj.ac/s/2236509](https://loj.ac/s/2236509)
```cpp
#include <iostream>
#include <cstring>
#include <algorithm>
#include <deque>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <unordered_map>
#include <cmath>
#include <bitset>
#include <iterator>
#include <random>
#include <iomanip>
#include <cctype>
#include <array>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<ll, 3> arr;
const ll MAXN = static_cast<ll>(1e6) + 10;
char S[MAXN];
// rk为第i个后缀子串排第几?
ll rk[MAXN], sa[MAXN], tmp[MAXN];
ll N;
int K;
bool comp_sa(ll a, ll b) {
if (rk[a] != rk[b]) {
return rk[a] < rk[b];
} else {
// 其实小于N就是对的,也不用考虑最后一位处理了
int ra = a + K <= N ? rk[a + K] : -1;
int rb = b + K <= N ? rk[b + K] : -1;
return ra < rb;
}
}
void cal_sa() { // 计算后缀数组
// tmp要访问到第N个数
for (int i = 0; i <= N; ++i) {
rk[i] = i < N ? S[i] : -1;
sa[i] = i; // 还没有排序的后缀数组
}
for (K = 1; K <= N; K = K * 2) {
sort(sa, sa + N, comp_sa);
// 排在sa最前面的rk肯定也是最前面的
tmp[sa[0]] = 0;
for (int i = 0; i < N; ++i) {
// tmp[sa[0]]排第一个,tmp[sa[0]]为第
// 如果确实小,rk+1(因为这个rk要排logn次,不是一次排出来的)
tmp[sa[i + 1]] = tmp[sa[i]] + (comp_sa(sa[i], sa[i + 1]) ? 1 : 0);
}
for (int i = 0; i < N; ++i) {
rk[i] = tmp[i];
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> S;
N = strlen(S);
cal_sa();
for (int i = 0; i < N; ++i) {
cout << sa[i] + 1 << ' ';
}
cout << '\n';
return 0;
}
```
## 心路历程(WA,TLE,MLE……)
AC(其实这个代码里有UB,我越界访问了,不过问题不大)
[https://www.luogu.com.cn/record/198321780](https://www.luogu.com.cn/record/198321780)
虽然洛谷的AC了,但是loj的出了问题
hack数据
00000
[https://loj.ac/s/2236497](https://loj.ac/s/2236497)
```cpp
#include <iostream>
#include <cstring>
#include <algorithm>
#include <deque>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <unordered_map>
#include <cmath>
#include <bitset>
#include <iterator>
#include <random>
#include <iomanip>
#include <cctype>
#include <array>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
typedef array<ll,3> arr;
const ll MAXN=static_cast<ll>(1e6)+10;
string S;
// rk为第i个后缀子串排第几?
ll rk[MAXN],sa[MAXN],tmp[MAXN];
ll N;
int K;
bool comp_sa(ll a,ll b){
if(rk[a]!=rk[b]){
return rk[a]<rk[b];
}else{
int ra= a+K<=N ? rk[a+K]:-1;
int rb= b+K<=N ? rk[b+K]:-1;
return ra<rb;
}
}
void cal_sa(){ // 计算后缀数组
// tmp要访问到第N个数
for(int i=0;i<=N;++i){
rk[i]=S[i];
sa[i]=i; // 还没有排序的后缀数组
}
for(K=1;K<=N;K=K*2){
sort(sa, sa+N, comp_sa);
// 排在sa最前面的rk肯定也是最前面的
tmp[sa[0]]=0;
for(int i=0;i<N;++i){
// tmp[sa[0]]排第一个,tmp[sa[0]]为第
// 如果确实小,rk+1(因为这个rk要排logn次,不是一次排出来的)
tmp[sa[i+1]]=tmp[sa[i]]+(comp_sa(sa[i], sa[i+1]) ? 1:0);
}
for(int i=0;i<N;++i){
rk[i]=tmp[i];
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>S;
N=S.size();
cal_sa();
for(int i=0;i<N;++i){
cout<<sa[i]+1<<' ';
}
cout<<'\n';
return 0;
}
```