题意
后缀数组 (SA) 是一种重要的数据结构,通常使用倍增或者DC3算法实现,这超出了我们的讨论范围。在本题中,我们希望使用快排、Hash与二分实现一个简单的 O(n log^2n ) 的后缀数组求法。详细地说,给定一个长度为 n 的字符串S(下标 0~n-1),我们可以用整数 k(0≤k<n) 表示字符串S的后缀 S(k~n-1)。把字符串S的所有后缀按照字典序排列,排名为 i 的后缀记为 SA[i]。额外地,我们考虑排名为 i 的后缀与排名为 i-1 的后缀,把二者的最长公共前缀的长度记为 Height[i]。我们的任务就是求出SA与Height这两个数组。
字符串,长度不超过30万。
分析
此题数据范围较小,可以用hash。
求sa:sort+二分hash加速比较,时间复杂度\(O(N \log^2 N)\)。
求height:二分hash,时间复杂度\(O(N \log N)\)
明显比倍增+基数排序慢。
代码
#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
rg T data=0,w=1;
rg char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') w=-1;
ch=getchar();
}
while(isdigit(ch))
data=data*10+ch-'0',ch=getchar();
return data*w;
}
template<class T>il T read(rg T&x){
return x=read<T>();
}
typedef unsigned long long ull;
using namespace std;
co int N=3e5+1,P=131;
ull f[N],power[N];
int sa[N],rk[N],height[N],n;
char str[N];
ull get_hash(int l,int r){
return f[r]-f[l-1]*power[r-l+1];
}
int lcp(int x,int y){
int l=0,r=min(n-x+1,n-y+1);
while(l<r){
int mid=(l+r+1)>>1;
if(get_hash(x,x+mid-1)==get_hash(y,y+mid-1)) l=mid;
else r=mid-1;
}
return l;
}
bool cmp(int x,int y){
int l=lcp(x,y);
return str[x+l]<str[y+l];
}
void calc_height(){
for(int i=2;i<=n;++i){
height[i]=lcp(sa[i-1],sa[i]);
}
}
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
scanf("%s",str+1);
n=strlen(str+1);
n=strlen(str+1);
power[0]=1;
for(int i=1;i<=n;++i){
sa[i]=i;
f[i]=f[i-1]*P+str[i];
power[i]=power[i-1]*P;
}
sort(sa+1,sa+n+1,cmp);
calc_height();
for(int i=1;i<=n;++i)
printf("%d ",sa[i]-1);
puts("");
for(int i=1;i<=n;++i)
printf("%d ",height[i]);
return 0;
}