【洛谷P3809】【模板】后缀排序
题目
题目链接:https://www.luogu.com.cn/problem/P3809
读入一个长度为 \(n\) 的由大小写英文字母或数字组成的字符串,请把这个字符串的所有非空后缀按字典序从小到大排序,然后按顺序输出后缀的第一个字符在原串中的位置。位置编号为 \(1\) 到 \(n\)。
思路
倍增求 SA 数组。
推荐 Blog 或者蓝书。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1000010;
int n,m,x[N],y[N],sa[N],c[N];
char ch[N];
void SA()
{
for (int i=1;i<=n;i++) x[i]=ch[i],c[x[i]]++;
for (int i=2;i<=m;i++) c[i]+=c[i-1];
for (int i=n;i>=1;i--) sa[c[x[i]]--]=i;
for (int k=1;k<=n;k<<=1)
{
int num=0;
for (int i=n-k+1;i<=n;i++) y[++num]=i;
for (int i=1;i<=n;i++) if (sa[i]>k) y[++num]=sa[i]-k;
for (int i=1;i<=m;i++) c[i]=0;
for (int i=1;i<=n;i++) c[x[i]]++;
for (int i=2;i<=m;i++) c[i]+=c[i-1];
for (int i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
swap(x,y);
x[sa[1]]=1; num=1;
for (int i=2;i<=n;i++)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k]) ? num : ++num;
m=num;
if (m==n) break;
}
}
int main()
{
scanf("%s",ch+1);
n=strlen(ch+1); m=122;
SA();
for (int i=1;i<=n;i++)
printf("%d ",sa[i]);
return 0;
}