什么是后缀数组
- 我们有时会需要判断多个字符串后缀的的字典序大小,此时我们可以用后缀数组维护。
原理
- 我们利用倍增的思想,先比较长度为
len
的字符串的字典序排名,之后在把他们合并起来。
- 具体的合并操作和基数排序是类似的,由于基数排序的稳定性,我们先以第一关键字排序再以第二关键字排序会得到一个”稳定”的序列。所以我们可以把
[l,len+l]
的字典序排名为第一关键字把
[len+l+1,r]
的字典序排名为第二关键字来排序,由于第一关键字就是上一层的把第二关键字,于是我们每次都排一下第二关键字即可。
void init_Sa(int n,int m){
for(int i=1;i<=m;i++)cnt[i]=0;
for(int i=1;i<=n;i++)++cnt[x[i]=S[i]];
for(int i=1;i<=m;i++)cnt[i]+=cnt[i-1];
for(int i=1;i<=n;i++)sa[cnt[x[i]]--]=i;
for(int len=1;len<=n;len<<=1){
int p=0;
for(int i=n-len+1;i<=n;i++)y[++p]=i;
for(int i=1;i<=n;i++)if(len<=sa[i])y[++p]=sa[i]-len;
for(int i=1;i<=m;i++)cnt[i]=0;
for(int i=1;i<=n;i++)cnt[x[i]]++;
for(int i=1;i<=m;i++)cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--)sa[cnt[x[y[i]]]--]=y[i];
int R=1;swap(x,y);x[sa[1]]=1;
for(int i=2;i<=n;i++)
x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+len]==y[sa[i]+len])?R:++R;
if((m=R)>=num)break;
}for(int i=1;i<=n;i++)rk[sa[i]]=i;
}