【进阶】字符串问题一大利器——后缀数组详解

如果想了解更多内容,欢迎关注我的微信公众号:信息学竞赛从入门到巅峰。

戳这里获得更好的阅读体验哦!

今天,我们来介绍一下解决字符串问题的一大利器——后缀数组。

几个定义

为了下文表示的方便,我们需要先达成几个共识。

1、字符串的位置从0开始标号,一直到n-1

2、后缀i,表示从i...n-1这些字符按顺序组成的字符串。显然,该字符串是原字符串的一个后缀。

3、字符串的大小比较:从第0个位置开始比较。如果相同,继续往后比;如果不同,则当前位置字符ASCII码大的对应字符串更大。如果仍无法比较大小,则长度长的字符串更大,否则两者相等。举个栗子:

后缀数组是啥

后缀数组,顾名思义,就是把一个字符串的每一个后缀都进行排序。在这个算法中,我们需要处理得到两个数组,第一个数组记录的排名第i的后缀是哪一个(sa数组),第二个数组是第i个后缀的排名是多少。

举个栗子:

sa数组就是我们通常所说的后缀数组,而rank数组可以通过sa数组快速求得。我们这个算法就是为了快速地求出sa数组的值。

后缀数组怎么求

求解后缀数组有两种方法:倍增算法,DC3算法。

其中,倍增算法的时间复杂度是O(NlogN)的,程序简单,算法过程易于理解。而DC3算法的时间复杂度是O(N),数据量大的时候,效率比倍增算法有显著提升,但是缺点在于DC3算法原理较难理解,代码冗长。所以,这里我们讲解倍增算法

下面进入正题(为了防止格式错乱,采用图片的形式来讲解)。

Code

#include <bits/stdc++.h>
#define maxn 200005
using namespace std;
int a1[maxn],a2[maxn],ss[maxn];
bool cmp(int *r,int i,int j,int len)
{
    return (r[i]==r[j] && r[i+len]==r[j+len]);
  //判断两个字符串时候完全一样 
  //即前缀和后缀是否一样 
}
void sa_get(int *r,int *sa,int n,int m)
{
    int i,j,p,*x=a1,*y=a2,*t;//使用指针在后面交换的时候直接交换指针
    //x[i]相当于rank数组,表示第i个排第几 
    //y[i]表示当前按照后半部分排好的序列,储存的是前半部分的起点
    for (i=0;i<m;++i)ss[i]=0;
    for (i=0;i<n;++i)ss[x[i]=r[i]]++;
    for (i=1;i<m;++i)ss[i]+=ss[i-1];
    for (i=n-1;i>=0;--i)sa[--ss[x[i]]]=i;
  //对后缀进行排序(刚开始时只有一个字符) 
  //桶排不会改变元素的相对位置 
    for (j=1,p=1;p<n;j*=2,m=p)
    {
        for (p=0,i=n-j;i<n;++i)y[p++]=i;//后面j个的后缀为0,一定最小 
        for (i=0;i<=n;++i)if (sa[i]>=j)y[p++]=sa[i]-j;
    //按照后缀从小到大的顺序排列前缀 
    //先确定后缀的顺序
        for (i=0;i<m;++i)ss[i]=0;
        for (i=0;i<n;++i)ss[x[y[i]]]++;
        for (i=1;i<m;++i)ss[i]+=ss[i-1];
        for (i=n-1;i>=0;--i)sa[--ss[x[y[i]]]]=y[i];//对前缀进行排序 
        for (t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;++i)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;//更新x的值 
    }
    return;
}
char s[maxn];
int sa[maxn],sum,num[maxn];
int main ()
{
    scanf ("%s",s);
    sum=strlen(s);
    for (int b=0;b<sum;++b)num[b]=s[b]-'a'+1;
    //由于在末尾添加最小字符,所以这里字符串从0开始
    //排序后的sa数组第一个是我们添加的字符,所以有效数组从下标1开始    
    num[sum++]=0;//记得一定要在最后补一个最小的字符
    sa_get(num,sa,sum,128);
    for (int b=1;b<sum;++b)printf ("%d ",sa[b]+1);puts("");
    return 0;
}
原创文章 77 获赞 18 访问量 9867

猜你喜欢

转载自blog.csdn.net/Timothy023/article/details/105859239