【题解】后缀数组
题目描述凑字数
题目描述
后缀数组 (\(SA\)) 是一种重要的数据结构,通常使用倍增或者 \(DC3\) 算法实现,这超出了我们的讨论范围。在本题中,我们希望使用快排、\(Hash\) 与二分实现一个简单的 \(O(n \ log^2n )\) 的后缀数组求法。详细地说,给定一个长度为 \(n\) 的字符串 \(S\)(下标 \(0\)~\(n-1\)),我们可以用整数 \(k(0\le k<n)\) 表示字符串S的后缀 \(S(k\)~\(n-1)\)。把字符串S的所有后缀按照字典序排列,排名为 \(i\) 的后缀记为 \(SA[i]\)。额外地,我们考虑排名为 \(i\) 的后缀与排名为 \(i-1\) 的后缀,把二者的最长公共前缀的长度记为 \(Height[i]\)。我们的任务就是求出 \(SA\) 与 \(Height\) 这两个数组。
输入格式
一个字符串,长度不超过30万。
输出格式
第一行为数组 \(SA\),相邻两个整数用1个空格隔开。
第二行为数组 \(Height\),相邻两个整数用1个空格隔开,特别地,假设 \(Height[1]=0\)。
样例
样例输入
ponoiiipoi
样例输出
9 4 5 6 2 8 3 1 7 0
0 1 2 1 0 0 2 1 0 2
数据范围与提示
样例解释
排名第一(最小)的后缀是 \(9\)(\(S[9\)~\(9]\),即字符串 \(i\)),第二的是后缀 \(4\)(\(S[4\)~\(9]\),即字符串 \(iiipoi\)),第三的是后缀 \(5\)( \(S[5\)~\(9]\),即字符串 \(iipoi\))以此类推。\(Height[2]\) 表示排名第 \(2\) 与第 \(1\) 的后缀的最长公共前缀,长度为 \(1\) ,\(Height[3]\) 表示排名第 \(3\) 与第 \(2\) 的后缀的最长公共前缀,长度为 \(2\) ,以此类推。
Solution
这不就一道 裸的 字符串Hash嘛。
对 \(s[x]\) 与 \(s[y]\) 的最长公共前缀进行二分,其中 \(s\) 为输入的字符串。
Q:不是求后缀吗,怎么就变成前缀了?
A:是按后缀字典序排序,对于两个串的字典序排序,若两个串的首字符相同,忽略该字符,比较下一个字符,直到两个串首字符不同或其中一个串为空为止。
二分时会用到 \(Hash\) :比较 \(s[x]\)~\(s[x+mid]\) 与 \(s[y]\)~\(s[y+mid]\) 是否为同一个前缀。其中 \(x\)、\(y\) 为 \(\operatorname{cmp}\) 提供的参数,也就是需要进行比较的字符串首位下标。
此时的二分求的是两个串的最长公共前缀长度,在求 \(Height\) 时也可以用到。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
using std::sort;
typedef unsigned long long ull;
const int maxn=1e6+5;
const int p=13331;
int len;
char s[maxn];
int sa[maxn];
ull hash[maxn],q[maxn];
ull Hash(int l,int r){
return hash[r]-hash[l-1]*q[r-l+1];
}
int max(int x,int y){
return x>y?x:y;
}
int binary_answer(int x,int y){
if(s[x]!=s[y])return 0;
int l=0,r=len-1-max(x,y),mid;
while(l<r){
mid=l+r+1>>1;
if(Hash(x,x+mid)==Hash(y,y+mid)&&x+mid<len&&y+mid<len)
l=mid;
else r=mid-1;
}
return l+1;
}
bool cmp(int x,int y){
int l=binary_answer(x,y);
return s[x+l]<s[y+l];
}
int main(){
scanf("%s",s);
len=strlen(s);
q[0]=1;
hash[0]=s[0]-'a'+1;
for(int i=1;i<len;++i){
sa[i]=i;
hash[i]=hash[i-1]*p+s[i]-'a'+1;
q[i]=q[i-1]*p;
}
sort(sa,sa+len,cmp);
for(int i=0;i<len;++i)
printf("%d ",sa[i]);
printf("\n0 ");
for(int i=1;i<len;++i)
printf("%d ",binary_answer(sa[i],sa[i-1]));
return 0;
}
—— · EOF · ——
真的什么也不剩啦 😖