后缀数组(SA)学习笔记
Demeanor_Roy · · 个人记录
- 像这种抽象算法还是记录一下吧。
符号约定
算法作用:
-
求出
sa 数组和height 数组。 -
借用
height 数组求任意两个后缀的最长公共前缀。
具体流程
SA 算法分为两部分。分别是求解
第一部分以倍增为基本思想:
假设我们已经求出了只考虑每个后缀前
现在我们来看每一次倍增具体如何实现。既然每一个后缀要考虑前
而得到按两个关键字分别排序的数组后,我们只需用基数排序进行一次双关键字排序即可完成流程。值得注意的是,我们在每次倍增后,需将前
这部分代码如下:
inline void SA()
{
for(int i=1;i<=n;i++) c[x[i]=s[i]]++;
for(int i=1;i<=m;i++) c[i]+=c[i-1];
for(int i=n;i>=1;i--) sa[c[x[i]]--]=i;
for(int k=1;k<=n;k<<=1)
{
int num=0;
for(int i=n-k+1;i<=n;i++) y[++num]=i;
for(int i=1;i<=n;i++) if(sa[i]>k) y[++num]=sa[i]-k;
for(int i=1;i<=m;i++) c[i]=0;
for(int i=1;i<=n;i++) c[x[i]]++;
for(int i=1;i<=m;i++) c[i]+=c[i-1];
for(int i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
swap(x,y);
num=x[sa[1]]=1;
for(int i=2;i<=n;i++) x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
if(num==n) break;m=num;
}
}
第二部分则需要一个结论。
- 断言:
height[rk_i] \geq height[rk_{i-1}] - 1 。
此结论易证,此处不详细说明。有了此结论后,我们只需从前往后求解
再考虑如何用
二者关系大于等于显然,小于等于可以用夹逼法证明。于是我们维护