字符串算法之后缀数组

24 篇文章 0 订阅
1 篇文章 0 订阅

前言

字符串算法一直是我最不愿碰的东西,包括DP。

什么是后缀?

”ababs”
中所有的后缀串为”ababs”,”babs”,”abs”,”bs”,”s”
我们按照字典序排列即为
”ababs”,’abs”,”babs”,”bs”,”s”
而后缀数组就是用来求后缀的字典序的

定义

SA[i]为排名为i的后缀第一个字符在主串里的位置.
譬如上面的例子
SA[1]=1,SA[2]=3,SA[3]=2,SA[4]=4,SA[5]=5
Rank[i]记录首字符在i位置的后缀的排名
很明显SA与Rank互为逆运算
简单讲:
SA是排名第几的位置
Rank是此位置排名第几

实现

首先我们用基数排序求到每个字符串中的字符的名次。这里我们就以aabaaaab为例,如图:
这里写图片描述
第一轮之后的结果就是这样。那么第二轮,就是对每个后缀的前两个字符进行排序。因为每个单字符的名次已经得出。就相当于对一个二元组(x,y)进行排序,且以 x 为第一关键字,以 y 为第二关键字。这么排序之后就得到了这么一幅图:
这里写图片描述
接下来继续倍增,对前四个字符进行排序,此时依旧相当于对一个二元组(x,y)排序,排序规则相同。此时的 x 和 y 分别表示前两个字符的名次和第三、四个字符的名次(在第二次排序已经将其全部求出),所以同上进行排序,就能得到这样的一幅图:
这里写图片描述
此时我们发现每个排名都不相同,即可停止倍增了。
这里给出构造后缀数组的代码

int make_SA(int n)
{
    int m=150;//确定边界 
    for(int i=1;i<=n;i++) t[x[i]=s[i]]++;
    for(int i=1;i<=m;i++) t[i]+=t[i-1];
    for(int i=n;i>=1;--i) SA[t[x[i]]--]=i;
    for(int k=1;k<=n;k<<=1)
    {
      int p=0;
      for(int i=0;i<=m;i++)y[i]=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=0;i<=m;i++) t[i]=0;
      for(int i=1;i<=n;i++) t[x[y[i]]]++;
      for(int i=1;i<=m;i++) t[i]+=t[i-1];
      for(int i=n;i>=1;i--) SA[t[x[y[i]]]--]=y[i];
      swap(x,y);
      x[SA[1]]=p=1;
      for(int i=2;i<=n;i++)
      x[SA[i]]=check(SA[i],SA[i-1],k)?p:++p;
      if(p>=n)break;
      m=p;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值