后缀数组(SA)做题记录

发布时间 2023-08-01 21:32:04作者: Hanghang007

SA 真的是个好东西,好呀好东西。

基础定义:

$sa$ 数组:后缀排序后排名为 $i$ 的后缀的起始位置下标。

$rk$ 数组:起始下标为 $i$ 的后缀的排名。

$height$ 数组:后缀排序后排名为 $i$ 和 $i-1$ 的最长公共前缀长度(Lcp)

模板:

char ch[N];
struct Suffix_Array
{
    ll m=200,X[N],Y[N],c[N],num[N],sa[N],height[N],rk[N],lg[N],st[N][23];
    void Sa()
    {
        for(int i=1;i<=n;i++)X[i]=ch[i],c[X[i]]++;
        for(int i=2;i<=m;i++)c[i]+=c[i-1];
        for(int i=n;i>=1;i--)sa[c[X[i]]--]=i;
        for(int k=1,p=0;k<=n;k=k<<1,p=0)
        {
            for(int i=n-k+1;i<=n;i++)Y[++p]=i;
            for(int i=1;i<=n;i++)if(sa[i]>k)Y[++p]=sa[i]-k;
            for(int i=1;i<=m;i++)c[i]=0;
            for(int i=1;i<=n;i++)c[X[i]]++;
            for(int i=2;i<=m;i++)c[i]+=c[i-1];
            for(int i=n;i>=1;i--)sa[c[X[Y[i]]]--]=Y[i],Y[i]=0;
            swap(X,Y);p=1;X[sa[1]]=1;
            for(int i=2;i<=n;i++)
            {
                if(Y[sa[i]]==Y[sa[i-1]]&&Y[sa[i]+k]==Y[sa[i-1]+k])X[sa[i]]=p;
                else X[sa[i]]=++p;
            }
            if(p==n)return;m=p;
        }
    }
    void Height()
    {
        ll k=0;
        for(int i=1;i<=n;i++)rk[sa[i]]=i;
        for(int i=1;i<=n;i++)
        {
            if(rk[i]==1)continue;
            if(k)k--;
            int j=sa[rk[i]-1];
            while(i+k<=n&&j+k<=n&&ch[i+k]==ch[j+k])k++;
            height[rk[i]]=k;
        }
    }
    void St()
    {
        for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
        for(int i=1;i<=n;i++)st[i][0]=height[i];
        for(int j=1;j<=20;j++)for(int i=1;i+(1<<j)-1<=n;i++)
            st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
    }
    int Lcp(int l,int r) //原数组中 l,r的lcp长度 
    {
        if((l=rk[l])>(r=rk[r]))swap(l,r); //若是直接在后缀数组sa[i]上求就删除 
        int d=lg[r-l++];
        return min(st[l][d],st[r-(1<<d)+1][d]); 
    }
}SA;
View Code