/*P1860 后缀数组
时间: 1000ms / 空间: 131072KiB / Java类名: Main
描述
我们定义一个字符串的后缀suffix(i)表示从s[i]到s[length(s)]这段子串。
后缀数组(Suffix array)SA[i]中存放着一个排列,满足suffix(sa[i])<suffix(sa[i+1]) 按照字典序方式比较
定义height[i]表示suffix(sa[i])与suffix(sa[i-1])之间的最长公共前缀长度,其中height[1]=0
你的任务就是求出SA和height这两个数组。字符串长度<=200000
输入格式
一行,为描述中的字符串(仅会出现小写字母)
输出格式
共两行,每行n个数,第一行为sa[i],第二行为height[i],其中每行的数均用空格隔开
测试样例1
输入
aabaaaab
输出
4 5 6 1 7 2 8 3
0 3 2 3 1 2 0 1*/
//本题算法就是题目名,而且还是后缀数组最简单的应用。
//关于后缀数组我不想多说,推荐 算法合集之《后缀数组——处理字符串的有力工具》
//有时真想有人给我讲讲,自学是非常非常痛苦的。
//但有2个点超时,不过不管了。
#include<cstdio>
#include<iostream>
#include<cstring>
#define N 200008
using namespace std;
int n,sa[2][N],rk[2][N],a[N],v[N],k,h[N];
char ch[N];
void geng(int sa[N],int rk[N],int SA[N],int RK[N])
{
for(int i=1;i<=n;i++)
v[rk[sa[i]]]=i;
for(int i=n;i;i--)
if(sa[i]>k)
SA[v[rk[sa[i]-k]]--]=sa[i]-k;
for(int i=n-k+1;i<=n;i++)
SA[v[rk[i]]--]=i;
for(int i=1;i<=n;i++)
RK[SA[i]]=RK[SA[i-1]]+(rk[SA[i]]!=rk[SA[i-1]]||rk[SA[i]+k]!=rk[SA[i-1]+k]);
return;
}
int main()
{
gets(ch+1);
n=strlen(ch+1);
for(int i=1;i<=n;i++)
a[i]=ch[i]-‘a‘+1;
int p=0,q=1;
for(int i=1;i<=n;i++)
v[a[i]]++;
for(int i=2;i<27;i++)
v[i]+=v[i-1];
for(int i=1;i<=n;i++)
sa[p][v[a[i]]--]=i;
for(int i=1;i<=n;i++)
rk[p][sa[p][i]]=rk[p][sa[p][i-1]]+(a[sa[p][i]]!=a[sa[p][i-1]]);
for(k=1;k<n;k<<=1)
{
geng(sa[p],rk[p],sa[q],rk[q]);
swap(p,q);
}
for(int i=1;i<=n;i++)
printf("%d ",sa[p][i]);
printf("\n");
k=0;
for(int i=1;i<=n;i++)
if(rk[p][i]==1)
h[1]=0;
else
{
int j=sa[p][rk[p][i]-1];
for(;a[i+k]==a[j+k];k++);
h[rk[p][i]]=k;
if(k>0)
k--;
}
for(int i=1;i<=n;i++)
printf("%d ",h[i]);
return 0;
}