后缀排序学习笔记

版权声明: https://blog.csdn.net/qq_16267919/article/details/82108744

建议参考:后缀数组——处理字符串的有力工具
后缀排序即对一个字符串的每一个后缀进行排序,如果暴力进行排序,考虑到字符串比较的复杂度,效率至少是 O ( n 2 ) 级别的.
考虑依次对每个位置开始的 2 i 个字符进行分组,把他们看成一个字符串,从小到大枚举 i 进行处理, s j , j + 2 i 1 的排名即 ( s j , j + 2 i 1 1 + s j + 2 i 1 , j + 2 i 1 ) 的排名,使用基数排序即可做到 O ( n l o g 2 n ) 的复杂度.
s a [ i ] 表示第 i 大的后缀的位置

bool cmp(LL *x,LL a,LL b,LL l)
{
    return x[a]==x[b] && x[a+l]==x[b+l];
}
void px()
{
    LL i,j,p,*x=X,*y=Y;
    fo(i,0,n-1) buc[x[i]=s[i]]++;
    fo(i,1,M) buc[i]+=buc[i-1];
    fd(i,n-1,0) sa[--buc[s[i]]]=i;//从大到小枚举
    for(j=1,p=0;p<n;j<<=1)
    {
        p=0;//别忘了
        fo(i,n-j,n-1) y[p++]=i;
        fo(i,0,n-1) if (sa[i]>=j) y[p++]=sa[i]-j;
        fo(i,0,n-1) _rk[i]=x[y[i]];
        qk(buc);
        fo(i,0,n-1) buc[_rk[i]]++;
        fo(i,1,M) buc[i]+=buc[i-1];
        fd(i,n-1,0) sa[--buc[_rk[i]]]=y[i];//从大到小枚举
        swap(x,y); p=1; x[sa[0]]=0; y[n]=-1;//别忘了
        fo(i,1,n-1)
        x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
    }
}

h [ i ] 表示第 i 大的后缀和第 i 1 大的后缀的最长公共前缀,那么有 h [ r k [ i + 1 ] ] >= h [ r k [ i ] ] ,可以这样求

    fo(i,0,n-1) rk[sa[i]]=i;
    LL j=0;
    fo(i,0,n-1)
    {
        if (!rk[i]) continue;
        j=j?j-1:j;
        fo(j,j,n-1)
        if (s[i+j]!=s[sa[rk[i]-1]+j]) break;
        h[rk[i]]=j;
    }

SPOJ_NSUBSTR
题意:求长度为 1 n 的重复次数最多子串的重复次数(可以相交)
先后缀排序求出 h 数组,用单调栈左右各扫一遍求出每个位置 i 是哪一段极长区间的最小值,区间长度就是 a n s [ i ] 的值

猜你喜欢

转载自blog.csdn.net/qq_16267919/article/details/82108744
今日推荐