关于我后缀数组看了两个小时无数篇讲解才自己YY出来这件事,所以写个笔记纪念一下
假设这是我给自己写的,假设如果有人看那么一定已经知道后缀数组干嘛了
倍增法求后缀数组
定义几个数组
rk[]:表示后缀i的当前排名
sa[]:表示当前排名为i的后缀的第一个字母的位置
tob[]:基数排序用的桶
sec[]:表示按照第二关键字排序后排名为i的后缀的第一个字母的位置
我们直接对着代码讲?
Rep(i,1,n)++tob[rk[i]=s[i]];
Rep(i,1,M)tob[i]+=tob[i-1];
Dwn(i,n,1)sa[tob[rk[i]]--]=i;
for(int w=1;;w<<=1){
int p=0;
for(int i=n-w+1;i<=n;i++)sec[++p]=i;
for(int i=1;i<=n;i++)if(sa[i]>w)sec[++p]=sa[i]-w;
for(int i=1;i<=M;i++)tob[i]=0;
for(int i=1;i<=n;i++)++tob[rk[i]];
for(int i=1;i<=M;i++)tob[i]+=tob[i-1];
for(int i=n;i>=1;i--)sa[tob[rk[sec[i]]]--]=sec[i],sec[i]=0;
swap(rk,sec);
rk[sa[1]]=1,p=1;
for(int i=2;i<=n;i++){//sec==fir
rk[sa[i]]= ( sec[sa[i]]==sec[sa[i-1]] && sec[sa[i]+w]==sec[sa[i-1]+w]) ? p : ++p;
}
if(p==n)break;
M=p;
}
首先我们初始化每个后缀i的排名为其ascii码值,并按他们的rk桶排
对桶数组做前缀和表示每个排名占那些位置
再初始化sa数组,从后往前考虑每个后缀,后缀i的rank在桶中排第几,那么排第几的后缀就是i,也就是sa数组
然后考虑把当前两个长度为w的后缀合并起来排序
我们需要知道每个长为2w的后缀的第一关键字和第二关键字,实际上第一关键字就是后缀i的rk,第二关键字就是后缀i+w的rk
显然如果长度都不够w了,那么他们是没有第二关键字的,直接把他们排到前面去,也就是,按第二关键字排序时,排名为前1~w的后缀为后w个后缀
再考虑剩下能做为第二关键字的后缀,我们按照k长的后缀的排名依次考虑排名为1~n的后缀,如果排名为i的后缀开头位置比w大,那么它可以作为第二关键字出现,并且他在其他可能作为第二关键字的后缀中,排名最靠前,那么按第二关键字排序的时候,他作为第二关键字时所对应的后缀(也就是合并成的后缀)的排名就是++p,开头位置为sa[i]-w
然后我们清空桶准备排序
先按照所有后缀当前的rk,也就是合并时的第一关键桶排,然后前缀和求出占的位置
然后我们更新sa数组,其意义是
我们现在已经按第一关键字在桶中排好序了,我们需要考虑那些第一关键字相同的,再按第二关键字排序一遍
从n到1枚举,按第二关键字排序时,(排名为i的合并成的后缀的开头位置的第一关键字在桶中的排名)= k ,那么sa[k]就等于排名为i的合并成的后缀的开头位置
swap没有必要意义,可以当做存一下上一次的rk,然后求合并后的新rk
首先排第一的rk肯定是1,因为我们是给每个后缀的后边再添一个后缀,就很显然
然后我们考虑现在排第二到n的后缀
如果他和上一个一二关键字都相同,那么他们的rk相同,否则加一
显然每个后缀的长度都不同,最后rk值肯定是1~n
所有当当前的最大rk到n时,即没用并列的后缀了,我们就排好序了