[sa][后缀数组]关于后缀数组的若干技巧

简单入门

sa[i]表示将后缀从小到大排名后,第i小的后缀的位置
rank[i]表示i这个后缀的排名
height[i]表示sa[i]和sa[i-1]的lcp
lcp(i,j)表示sa[i]和sa[j]的lcp。则lcp(i,j)=min(height[i+1],……,height[j]).

求后缀数组的原理:
将每次我们求的后缀的长度*2,求这个长度的后缀的排名。则求这次的后缀的排名时可以使用以前的信息。如求位置为i,长度为j*2的后缀的排名,则可以通过比较位置i,长度为j的后缀(第一关键字)和位置i+j,长度为j的后缀(第二关键字)的排名(上一次长度的信息)来得到i位置长度为j的后缀的排名。

求后缀数组的方法:
那么如果求每个后缀时都直接保存上次的信息,然后快排,则效率为nlog^2n.使用基数排序的技巧来优化,使得效率达到nlogn。
记w[i]表示第一关键字排名为i的后缀数量,然后做一遍前缀和的值。即所有第一关键字为i时第二关键字最大时的后缀的排名。
x[i]表示上一轮排名后,i的排名。
暂时的rank[i]表示第二关键字排名第i小的第一关键字的位置。
则我们将第二关键字倒序枚举,即从大到小枚举第二关键字,对于第一关键字相等的后缀,第二关键字大的分配到的排名应该越大,即sa[w[rank[i]]–]=rank[i]。
给出样例:
对于基排我们有
10,11,12,13,先将十位个位分开丢进桶
1:0 1 2 3
则13分配到的排名应该尽量大,即sa[x]=(13的位置)和sa[y]=(12的位置),x>y。
后缀数组中的实现为,先枚举到第二关键字大的3,然后rank[]得到其第一关键字为1,w表示1这个桶中最大的那个数被取出时的排名,实现了13的排名取值。

给出模板:

inline void Sa()
{
    int m=127,u,v;
    for(int i=1;i<=m;++i) w[i]=0;
    for(int i=1;i<=n;++i) w[x[i]=sr[i]]++;
    for(int i=1;i<=m;++i) w[i]+=w[i-1];
    for(int i=n;i>=1;--i) sa[w[x[i]]--]=i;
    for(int j=1;j<=n;j*=2)
    {
        int cnt=0;
        for(int i=n-j+1;i<=n;++i) rank[++cnt]=i;
        for(int i=1;i<=n;++i)
        if(sa[i]>j) rank[++cnt]=sa[i]-j;
        for(int i=1;i<=m;++i) w[i]=0;
        for(int i=1;i<=n;++i) w[x[i]]++;
        for(int i=1;i<=m;++i) w[i]+=w[i-1];
        for(int i=n;i>=1;--i) sa[w[x[rank[i]]]--]=rank[i];
        m=0;
        for(int i=1;i<=n;++i)
        {
            u=sa[i];v=sa[i-1];
            if(x[u]!=x[v]||x[u+j]!=x[v+j]) ++m;
            rank[u]=m;
        }
        if(m==n) break;
        for(int i=1;i<=n;++i) x[i]=rank[i];
    }
    int j=0;
    for(int i=1;i<=n;++i)
    {
        v=sa[rank[i]-1];
        j=max(0,j-1);
        while(sr[i+j]==sr[v+j]) ++j;
        hei[rank[i]]=j;
    }
}

若干技巧(持续更新,有待发现

很多题不好做就先二分再想想怎么check

本质不同的子串个数为:
insa[i]hei[i] ,显然对于sa[i]这个位置,有hei[i]个相同的前缀,则之后贡献的是不同的

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值