利用倍增演算法的字尾陣列

2021-07-29 08:54:38 字數 2874 閱讀 3795

仍然不是很懂,貼篇文章放在這兒希望有朝一日能頓悟吧。

老天,乙個字尾陣列不知道看了多少天,最後終於還是看懂了啊!

最關鍵的就是一會兒下標表示排名,一會用數值表示排名繞死人了。

我不知道手跑了多少次才明白過來。其實我也建議初學者手跑幾遍,但是一定要注意陣列的意義,否則就是無用功。

s[ ]:輸入的字串,預處理的時候會在末尾加上乙個0

sa[ ]:它的下標就是字尾排名

x[ ] = t[ ]:用來儲存第一關鍵字排名,注意!它的數值是排名。初始時恰好是字串的ascii碼。字典序嘛!

y[ ] = t2[ ]:它的下標就是第二關鍵字排名,第二關鍵字是直接從sa[ ]當中提取的,關係極其密切

c[ ]:用來基數排序。初始值恰好是每種字元出現的次數。後來它的作用就跟基數排序密切相關,建議學習基數排序

有一點一定要注意!第二關鍵字來自sa[ ]陣列,但是第一關鍵字並不是來自sa[ ]陣列!這一點不知道迷惑了多少人,就是因為**裡給出的圖完全就是原理圖,不是**實現的圖,不搭噶的!

p.s. 為了優化時間空間,避免新開乙個中間陣列來複製t[ ]的值,採用了將它的指標x和t2[ ]的指標y交換的方法。注意這個時候t2[ ]已經沒有用了。

[cpp]view plain

copy

print

?#include 

#include 

#include 

using

namespace

std;  

const

intn = 1000, m = 130;  

char

s[n];  

intsa[n], t[n], t2[n], c[m], n;  

intrank[n], high[n];  

#define dbg

#ifdef dbg

intdb[n];  

void

debug(

int*f)  

printf("%3d"

, db[0]);  

for(

inti = 1; i 

);  

}  #endif

bool

cmp(

int*y, 

inti, 

intk)  

void

build(

intm)  

puts("}"

);  

#endif

//x的內容就是對應的第一關鍵字排名

//根據x的內容和y的下標進行合併,得到新的排名作為sa的下標

for(i = 0; i 

for(i = 0; i 

for(i = 1; i 

for(i = n-1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];  

#ifdef dbg

printf("sa get:["

);  

debug(sa);  

puts(""

);  

#endif

//按照sa的順序提取出老的x,計算新的x

swap(x, y);  

p = 1; x[sa[0]] = 0;//sa[0]一定是新增的字元0,排名萬年第0

for(i = 1; i 

//剪枝,此時x中已經沒有相同的值,sa被確定

if(p >= n) 

break

;  }  

}  void

get_high()  

}  void

pr()  

intmain()  

s[n-1] = 0;  

build(maxi+1);  

get_high();  

#ifdef dbg

pr();  

#endif

return

0;  

}  

根據這份**,輸入一些資料測試一下,仔細研究研究中間輸出。

方框代表裡面的值是下標,花括號代表是數值。它們都是和第一行紅色數字一一對應的。

我們暫時不去管第一關鍵字是怎樣計算出來的。

根據上面的程式,自己來填寫這張圖當中的數值。乙個乙個填寫就可以明白了。(x[ ]陣列的值就直接看圖上的,並且注意每乙個x[ ]陣列都是在上一層基數排序計算出來的)

sa[ ]的初始值恰好是根據字元出現次數乙個乙個來的,輕易就可以手跑出來。這就完成了一位數的基數排序。

藍色的字是第二關鍵字,正好是從sa[ ]當中提取出來的。黃色的箭頭表示沒有第二關鍵字,它們的排名是自左向右從0開始填的,要先填完這個再提取其他的第二關鍵字。再次強調,雖然有線,但是第一關鍵字並不是sa[ ]陣列當中的數!

然後給出的x[ ]和剛填完的y[ ]合併(綠色字型),計算出sa[ ]。這是兩位數的基數排序。

接下來繼續倍增,完成四位數的基數排序。(如果你困惑為什麼還是只有兩個數被線指著,建議閱讀**)

最後,其實本來是不用對八位數進行基數排序,因為這個時候新的x[ ]陣列(圖中倒數第二行)裡面已經沒有重複的排名了,而第一關鍵字是首要的,因此sa[ ]陣列被確定下來了。這裡可以加個剪枝,break一下。

在每一次得到sa[ ]陣列之後,計算新的x[ ],方法是按照sa[ ]當中的排名順序,(即sa[1...n])提取出舊的x[ ](注意此時它的名字叫做y[ ]了)來計算。如果某字串跟之前的那個完全一樣(即cmp()函式),排名就一樣(p-1)。

根據上面的話,再來自己填寫x[ ]陣列吧!

字尾陣列 倍增演算法模板

關於字尾陣列的資料,可以看noi2009國家集訓隊 羅穗騫 的 字尾陣列 處理字串的有力工具 suffix array 倍增演算法 o n lgn build sa n 1,注意n 1 getheight n n 8 num 注意num陣列最後一位值為0,其它位須大於0 rank rank 0 n ...

字尾陣列倍增演算法模板詳解

2009國家集訓隊 字尾陣列 處理字串的有力工具 羅穗騫 bool cmp int r,int a,int b,int l void init int r,int sa,int n,int m for i 0 i n i rk sa i i int k 0 for i 0 i n 1 h rk i ...

字尾陣列倍增法

字尾陣列 字尾陣列是處理字串的有力工具。字尾陣列是字尾樹的乙個非常精巧的替代品,它比字尾樹容易程式設計實現,能夠實現字尾樹的很多功能而時間複雜度也並不遜色,而且它比字尾樹所占用的記憶體空間小很多。可以說,在資訊學競賽中字尾陣列比字尾樹要更為實用。1.1 基本定義 子串 字串s的子串r i.j i j...