后缀数组
# 题意
详细地说,给定一个长度为 n 的字符串S(下标 0~n-1),我们可以用整数 k(0≤k<n0≤k<n) 表示字符串S的后缀 S(k~n-1)。
把字符串S的所有后缀按照字典序排列,排名为 i 的后缀记为 SA[i]。
额外地,我们考虑排名为 i 的后缀与排名为 i-1 的后缀,把二者的最长公共前缀的长度记为 Height[i]。
我们的任务就是求出SA与Height这两个数组。
# 题解
开始每个SA都是当前的下标
定义比较函数是从两个后缀的公共长度后第一个字符的大小比较
用二分求最长公共序列长度,
因为是最长用最后一个元素的二分板子
排序过后
再依次输出字典序排在第 i 位的的后缀的开头下标
已经他和他前面一个的最长公共序列长度
1 #include <bits/stdc++.h> 2 #define ull unsigned long long 3 using namespace std; 4 const int N=3e5+10,P=131; 5 char s[N]; 6 int sa[N]; 7 ull h[N],p[N]; 8 int n; 9 ull get(int l,int r){ 10 return h[r]-h[l-1]*p[r-l+1]; 11 } 12 int get_lcp(int a,int b){ 13 int l=0,r=min(n-a+1,n-b+1); 14 while(l<r){ 15 int mid=l+r+1>>1; 16 if(get(a,a+mid-1) == get(b,b+mid-1)) 17 l=mid; 18 else 19 r=mid-1; 20 } 21 return l; 22 } 23 bool cmp(int a,int b){ 24 int len=get_lcp(a,b); 25 int ava=a+len>n?INT_MIN:s[a+len]; 26 int bva=b+len>n?INT_MIN:s[b+len]; 27 return ava<bva; 28 } 29 int main(){ 30 ios::sync_with_stdio(0); 31 cin.tie(0); 32 cout.tie(0); 33 34 scanf("%s",s+1); 35 n=strlen(s+1); 36 p[0]=1; 37 for(int i=1;i<=n;i++){ 38 h[i]=h[i-1]*P+s[i]-'a'+1; 39 p[i]=p[i-1]*P; 40 sa[i]=i; 41 } 42 sort(sa+1,sa+n+1,cmp); 43 44 for(int i=1;i<=n;i++) 45 cout<<sa[i]-1<<' '; 46 cout<<endl; 47 48 for(int i=1;i<=n;i++){ 49 if(i==1) 50 cout<<"0 "; 51 else 52 cout<<get_lcp(sa[i-1],sa[i])<<' '; 53 } 54 cout<<endl; 55 }