后缀排序
即对字符串\(S\)的所有后缀根据字典序排序
实现
算法1:暴力排序
直接\(O(n)\)比较,时间复杂度\(O(n^2\log n)\)
算法2:倍增优化
我们考虑长为\(2^k\)的串的比较,该串可以分为前后均长\(2^{k-1}\)的串,那么只要知道这两个串的排名,就可以对所有\(2^k\)的串进行双关键字排序
于是每次记录\(rk[i]\)为\(S[i\sim i+2^k-1]\)的排名,则\(S[i\sim i+2^{k+1}-1]\)的两个关键字为\((rk[i],rk[i+2^k])\),倍增处理\(2^k\),每次排序即可
当某后缀无法拓展至\(2^k\)时,由于越界访问\(sa[]\),\(sa[]\)值为 \(0\),则该关键字无限小,正好对应了字典序排序中“空字符字典序无限小”,因此可以不用特判,开两倍空间即可
时间复杂度\(O(n\log^2n)\)
算法3:基数排序优化
尝试优化时,我们发现双关键字排序复杂度为\(O(n\log n)\),尝试从这里着手优化
于是想到了基数排序——时间复杂度\(O(n|A|)\),\(|A|\)为数的位数,这里为\(2\),于是排序复杂度变为\(O(n)\)
时间复杂度\(O(n\log n)\)
算法4:见OIwiki
后缀数组
\(sa[i]\)记录后缀排序后排名为\(i\)的后缀的编号
在字符串领域内有相当广泛的应用
height数组
定义,\(sa\)数组中相邻两项的最长公共前缀,即\(h[i]=LCP(sa[i],sa[i-1])\)
对于\(height\)数组,有公式\(h[rk[i]]\ge h[rk[i-1]]-1\)
因此可以线性求解\(height\)数组
代码:
for(rg int i=1,k=0;i<=n;i++) {if(rk[i]==1) {h[rk[i]]=0; continue;}if(k) --k;while(S[i+k]==S[sa[rk[i]-1]+k]) ++k;h[rk[i]]=k;}
\(height\)数组还有一个重要的公式:\(\displaystyle LCP(sa[i],sa[j])=\min\{h[i+1]\sim h[j]\}\)
\(height\)数组是解决一些子串问题的关键