后缀数组倍增算法代码及解释

来源:互联网 发布:淘宝your highness 编辑:程序博客网 时间:2024/05/22 05:23

经历

最近在学习后缀数组,我发现后缀数组那20多行代码10多个for,真的是太抽象了,基数排序又不是很会所以学起来异常艰难,在冥思苦想以及抱神犇(Dumpling)的大腿后终于有些理解了,将自己写的注释发出来帮助和我一样的人= =.(我觉得我对代码的讲解还是比较细了,但是我没有讲方法,只讲了代码,方法应该比较好懂.)

讲解

#include<queue>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;const int maxn=15000+10;const char msg[]="ABCDEFG";char s[maxn];int sa[maxn],t[maxn],t2[maxn],c[maxn],n;void build(int m){    int i,*x=t,*y=t2;    for(i=0;i<m;i++) c[i]=0;    for(i=0;i<n;i++) c[x[i]=s[i]]++;    for(i=1;i<m;i++) c[i] += c[i-1];    for(i=n-1;i>=0;i--)       sa[--c[x[i]]]=i;    /*    解读:c[x[i]]表示字符串中<=i的字符个数,那么i应该在第     c[x[i]]个(同字符并不影响),因为sa从0开始,所以名次减    一,但将这个拿走之后会少一个字符,c[x[i]]要-1,所以直接    -- .即这一趟按单个字母排 .     */    for(int k=1;k<n;k<<=1)     {   //倍增,将k凑成k*2长度,k>=n就没必要了        int p=0;        for(i=n-1;i>=n-k;i--) y[p++]=i;        /*        #define 第一关键字 G1        #define 第二关键字 G2         这是一些长度不够的后缀,G2排序时一定会排        在前面(G2全为'\0'(因为有'全'所以是n-k        而不是n-k*2+1,倍增出来的G2是经过合并的))         这里将n-k到n-1放进了y数组         */         for(i=0;i<n;i++)           if(sa[i]>=k) y[p++]=sa[i]-k;        /*        合并过程中需要将G2往前移k个位置,那么y         (现在是按G2在排)中,y中G2的相对排名和原来的G2是一        样的,所以直接复制过来,(也可以理解为sa并没        有提供n-k~n-1这一段的有效信息,因为G2是空的)         这里将0~n-k-1放进了y数组        综上,这两行将0~n-1不重不漏的放进了y数组.         */        for(i=0;i<m;i++) c[i]=0;        for(i=0;i<n;i++) c[x[y[i]]]++;        for(i=1;i<m;i++) c[i]+=c[i-1];        for(i=n-1;i>=0;i--)           sa[--c[x[y[i]]]]=y[i];        /*        这4行对G2已经排好序的y进行G1排序,仍然是基数        排序,不过因为G2变了(排单个字母时也可理解为        G2全为空),不能再用i而要用y[i],想一想如果G2        全空的话无论什么顺序都可以.         之所以要倒着加是保证按照G2已经排好的顺序,        因为c[x[y[i]]]是相同G1里面名次最大的那个        QAQ我终于懂了         */        swap(x,y);        p=1;x[sa[0]]=0;//sa[0]是编号最小的那个后缀         for(i=1;i<n;i++)          if(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k])            //G1==G'1&&G2==G'2             x[sa[i]]=p-1;          else x[sa[i]]=p++;        if(p>=n) break;        m=p;      }    return ;}void debug(){    for(int i=0;i<n;i++)    {        printf("%d ",i);        for(int j=sa[i];j<n;j++)        {            printf("%c",s[j]);        }        printf("\n");    }    return ;}int main(){    scanf("%s",s);    n=strlen(s);    build('}'+1);    debug();    return 0;}
1 0
原创粉丝点击