后缀数组复习小记

前言

复习小记差不多是写给自己看的,步骤都比较简略,主要总结要点。初学者的话还是去看罗穗骞的论文吧。
这个东西我初二就会了23333333

DA 算法构造

DA 就是倍增算法。
每一层都有长度相等的一二关键字。
SA 就是排名第 i 的后缀的开始位置, rank 就是第 i 个后缀的排名。
数组 x 代表某后缀在上一层的 rank ,数组 y 代表按第二关键字的 SA (位置是第一关键字开始处)。
Wv 就是第二关键字第 i 的后缀的第一关键字( x[y[i]] ), Ws 就是 Wv 映射后做前缀和。
SA 时倒着做来维护第二关键字的有序。
最后来处理这一轮的 x ,因为有些后缀在当前排序长度下会完全一样,所以用类似离散化的方法,比较函数 cmp 为:bool cmp(int *r,int i,int j,int l){return r[i]==r[j]&&r[i+l]==r[j+l];}
为了不特殊判断边界,我们只需将 r (关键字数组,这里我们将 x 复制到 y 上,将 y 作为关键字 r )至少开多一位就行了(如果满足r[i]==r[j],那至少 i+l1 j+l1 是没有到边界的)。
这里有个小优化,当离散化后发现所有后缀都互不相同时,我们可以直接退出循环。这个优化实测效果好到飞起。
然后我们就扫一遍求 rank 即可。
这里是 DA 算法的代码(短小精悍):

void DA()
{
    int mx=0;
    for (int i=0;i<n;i++)mx=max(x[i]=Wv[i+1]=s[i]-'a'+1,mx);
    for (int i=0;i<=mx;i++)Ws[i]=0;
    for (int i=1;i<=n;i++)Ws[Wv[i]]++;
    for (int i=1;i<=mx;i++)Ws[i]+=Ws[i-1];
    for (int i=n;i>=1;i--)SA[Ws[Wv[i]]--]=i-1;
    /*初始处理这一段,如果字符集很大,就换用快排*/
    int l=1,p;
    while (l<=n)
    {
        p=0;
        for (int i=n-l;i<n;i++)y[++p]=i;
        for (int i=1;i<=n;i++)if(SA[i]>=l)y[++p]=SA[i]-l;
        mx=0;
        for (int i=1;i<=n;i++)mx=max(Wv[i]=x[y[i]],mx);
        for (int i=0;i<=mx;i++)Ws[i]=0;
        for (int i=1;i<=n;i++)Ws[Wv[i]]++;
        for (int i=1;i<=mx;i++)Ws[i]+=Ws[i-1];
        for (int i=n;i>=1;i--)SA[Ws[Wv[i]]--]=y[i];
        for (int i=0;i<=n;i++)y[i]=x[i],x[i]=0;
        p=1;
        x[SA[1]]=1;
        for (int i=2;i<=n;i++)x[SA[i]]=cmp(SA[i-1],SA[i],l)?p:++p;
        if (p==n)break;//小优化
        l<<=1;
    }
    for (int i=1;i<=n;i++)rank[SA[i]]=i;
}

至于 DC3 ,我不会,一般出题人不会丧心病狂卡你的构造,如果实在不行,那估计正解就是 SAM (后缀自动机)了。

height 数组

我们定义 height 表示 length(LCP(suffix(SA[i]),suffix(SA[i1])))
然后令 rank[i]rank[j] ,那么 length(LCP(suffix(i),suffix(j))) 显然为

minjk=i+1height[k]

然后怎么线性求 height 呢?
我们令 h[i]=height[rank[i]] ,可证 h[i]h[i1]1 (证明见论文)。
然后我们就可以按照 h[1] h[2] …… h[n] 的顺序计算。这样时间复杂度显然为 O(n)
height 的代码如下:

void get_height()
{
    int k=0;
    for (int i=0;i<n;i++)
    {
        k=k?k-1:0;
        if (rank[i]==1)
            continue;
        int j=SA[rank[i]-1];
        while (i+k<n&&j+k<n&&s[i+k]==s[j+k])
            k++;
        height[rank[i]]=k;
    }
}

常见思路

首先肯定要搞出以上全部东西,然后按排序后的顺序来分析。
通常是单调栈把 height 数组一顿乱搞。
当然还有按照排序后的顺序数据结构维护一些神奇的东西,比如搞棵主席树处理区间问题。
据说 SA 数组搞出来后可以快速建出后缀树,反正我还不会,会了之后再更吧。

你可能感兴趣的:(算法,字符串,后缀数组,OI)