學完後綴數組後第一個模板題
後綴中:
sa[i] : 開頭爲i的後綴
rak[i]:排名爲i的後綴的開頭位置
#include<cstdio>
#include<cstring>
#include<algorithm>
const int MAXN = 1e6 + 10;
using namespace std;
char s[MAXN];
int N, M, rak[MAXN], sa[MAXN], tax[MAXN], tp[MAXN];
//tp[i] 第二關鍵字排名i的下標
//rak[i] 第一關鍵字排名i的下標。。最終是排名i的後綴的下標
//sa[i] 下標i的後綴的排名
void Debug() {
printf("*****************\n");
printf("下標"); for (int i = 1; i <= N; i++) printf("%d ", i); printf("\n");
printf("sa "); for (int i = 1; i <= N; i++) printf("%d ", sa[i]); printf("\n");
printf("rak "); for (int i = 1; i <= N; i++) printf("%d ", rak[i]); printf("\n");
printf("tp "); for (int i = 1; i <= N; i++) printf("%d ", tp[i]); printf("\n");
}
void Qsort() {
for (int i = 0; i <= M; i++) tax[i] = 0;
for (int i = 1; i <= N; i++) tax[rak[i]]++;
for (int i = 1; i <= M; i++) tax[i] += tax[i - 1];
for (int i = N; i >= 1; i--) sa[ tax[rak[tp[i]]]-- ] = tp[i];
//基數排序,sa[tax[z]--]=tp[i],更新排名
//z是第二關鍵字排名i的後綴 放在什麼位置。 tax[z]是第一關鍵字排名一樣的放在一起
}
void SuffixSort() {
M = 75;
for (int i = 1; i <= N; i++) rak[i] = s[i] - '0' + 1, tp[i] = i;
Qsort();
// Debug();
for (int w = 1, p = 0; p < N; M = p, w <<= 1) {
//w:當前倍增的長度,w = x表示已經求出了長度爲x的後綴的排名,現在要更新長度爲2x的後綴的排名
//p表示不同的後綴的個數,很顯然原字符串的後綴都是不同的,因此p = N時可以退出循環
p = 0;//這裏的p僅僅是一個計數器000
for (int i = N; i >= N-w+1; i--) tp[++p] = i;
//這裏一直沒看懂,其實就是後面的字符串後W是空的 所以優先級更高而已。
for (int i = 1; i <= N; i++) if (sa[i] > w) tp[++p] = sa[i] - w;
//這裏就是利用上一次求的長度w後綴的排名,這次的tp是後w,即i+w位置的rak所以從排名前的往後找,找的優先級一定更高
/* puts("-----");
puts("-----");
Debug();
puts("-----");
puts("-----"); */
Qsort();//此時我們已經更新出了第二關鍵字,利用上一輪的rak更新本輪的sa
swap(tp, rak);//這裏原本tp已經沒有用了
rak[sa[1]] = p = 1;
for (int i = 2; i <= N; i++)
rak[sa[i]] = (tp[sa[i - 1]] == tp[sa[i]] && tp[sa[i - 1] + w] == tp[sa[i] + w]) ? p : ++p;
//判斷合併後的後綴的前綴是否完全相同。相同就在rak裏相同
// Debug();
}
for (int i = 1; i <= N; i++)
printf("%d ", sa[i]);
}
int main() {
scanf("%s", s + 1);
N = strlen(s + 1);
SuffixSort();
return 0;
}