【進階】字符串問題一大利器——後綴數組詳解

如果想了解更多內容,歡迎關注我的微信公衆號:信息學競賽從入門到巔峯。

 

戳這裏獲得更好的閱讀體驗哦!

 

今天,我們來介紹一下解決字符串問題的一大利器——後綴數組。

 

幾個定義

爲了下文表示的方便,我們需要先達成幾個共識。

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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章