[**:
字尾陣列
【字尾陣列是處理字串的有力工具。字尾陣列是字尾樹的乙個非常精巧的替代品,它比字尾樹容易程式設計實現,能夠實現字尾樹的很多功能而時間複雜度也並不遜色,而且它比字尾樹所占用的記憶體空間小很多。可以說,在資訊學競賽中字尾陣列比字尾樹要更為實用。】
1.1、基本定義
子串:字串s的子串r[i..j],i≤j,表示r串中從i到j這一段,也就是順次排列r[i],r[i+1],…,r[j]形成的字串。
字尾:字尾是指從某個位置i開始到整個串末尾結束的乙個特殊子串。字串r的從第i個字元開始的字尾表示為suffix(i),也就是suffix(i)=r[i..len(r)]。
大小比較:關於字串的大小比較,是指通常所說的「字典順序」比較,也就是對於兩個字串u、v,令i從1開始順次比較u[i]和v[i],如果u[i]=v[i]則令i加1,否則若u[i]
<
v[i]則認為u
<
v,u[i]
>
v[i]則認為u
>
v(也就是v
<
u),比較結束。如果i
>
len(u)或者 i
>
len(v)仍比較不出結果,那麼若len(u)
<
len(v)則認為u
<
v,若 len(u)=len(v)則認為u=v,若len(u)
>
len(v)則 u
>
v。從字串的大小比較的定義來看,s的兩個開頭位置不同的字尾 u和v進行比較的結果不可能是相等,因為 u=v的必要條件len(u)=len(v)在這裡不可能滿足。
字尾陣列:字尾陣列sa是乙個一維陣列,它儲存1..n的某個排列sa[1],sa[2],……,sa[n],並且保證 suffix(sa[i])
<
suffix(sa[i+1]),1≤i
<
n。也就是將s的n個字尾從小到大進行排序之後把排好序的字尾的開頭位置順次放入sa中。
名次陣列:名次陣列rank[i]儲存的是suffix(i)在所有字尾中從小到大排列的「名次」。
簡單的說,字尾陣列是「排第幾的是誰?」,名次陣列是「你排第幾?」。容易看出,字尾陣列和名次陣列為互逆運算。如圖1所示。
設字串的長度為n。為了方便比較大小,可以在字串後面新增乙個字元,這個字元沒有在前面的字元中出現過,而且比前面的字元都要小。在求出名次數組後,可以僅用o(1)的時間比較任意兩個字尾的大小。在求出字尾陣列或名次陣列中的其中乙個以後,便可以用o(n)的時間求出另外乙個。任意兩個字尾如果直接比較大小,最多需要比較字元n次,也就是說最遲在比較第n個字元時一定能分出「勝負」。
1.2、倍增演算法
倍增演算法的主要思路是:用倍增的方法對每個字元開始的長度為2k的子字串進行排序,求出排名,即rank值。k從0開始,每次加1,當2k大於n以後,每個字元開始的長度為2k的子字串便相當於所有的字尾。並且這些子字串都一定已經比較出大小,即rank值中沒有相同的值,那麼此時的rank值就是最後的結果。每一次排序都利用上次長度為2k-1的字串的rank值,那麼長度為2k的字串就可以用兩個長度為2k-1的字串的排名作為關鍵字表示,然後進行基數排序,便得出了長度為2k的字串的rank值。以字串「aabaaaab」為例,整個過程如圖2所示。其中x、y是表示長度為2k的字串的兩個關鍵字。
具體實現:
int wa[maxn],wb[maxn],wv[maxn],ws[maxn];
int cmp(int *r,int a,int b,int l)
void da(int *r,int *sa,int n,int m)
這裡可以看到規定r[n-1]=0的好處,如果r[a]=r[b],說明以r[a]或r[b]開頭的長度為l的字串肯定不包括字元r[n-1],所以呼叫變數r[a+l]和r[b+l]不會導致陣列下標越界,這樣就不需要做特殊判斷。執行完上面的**後,rank值儲存在x陣列中,而變數p的結果實際上就是不同的字串的個數。這裡可以加乙個小優化,如果p等於n,那麼函式可以結束。因為在當前長度的字串中,已經沒有相同的字串,接下來的排序不會改變rank值。例如圖1中的第四次排序,實際上是沒有必要的。對上面的兩段**,迴圈的初始賦值和終止條件可以這樣寫:
for(j=1,p=1;p在第一次排序以後,rank陣列中的最大值小於p,所以讓m=p。
整個倍增演算法基本寫好,**大約25行。
演算法分析:倍增演算法的時間複雜度比較容易分析。每次基數排序的時間複雜度為o(n),排序的次數決定於最長公共子串的長度,最壞情況下,排序次數為logn次,所以總的時間複雜度為o(nlogn)。
字尾陣列(倍增法)
字尾陣列 suffix array 將某個字串的所有字尾按字典序排序後得到的陣列。演算法 樸素實現 直接將所有字尾進行排序,將n個長度為o n 的字串進行排序,時間複雜度o n 2 logn 倍增演算法 通過充分利用各個字尾之間的聯絡,將構造字尾陣列的最壞時間複雜度成功降至o n logn 倍增法實...
字尾陣列 倍增法詳解
主要是基於基數排序,如果基數排序沒弄懂 就會很難理解 首先從k 0開始,從字尾陣列裡面選取步長為2 k的字尾陣列的前子串 然後進行基數排序 如果排序後所有的名次陣列的值都不相同,那麼排序結束 否則,k 也就是步長翻倍 繼續排序。陣列sa sorted array 構造完成前表示關鍵字陣列,下標表示名...
字尾陣列 倍增演算法模板
關於字尾陣列的資料,可以看noi2009國家集訓隊 羅穗騫 的 字尾陣列 處理字串的有力工具 suffix array 倍增演算法 o n lgn build sa n 1,注意n 1 getheight n n 8 num 注意num陣列最後一位值為0,其它位須大於0 rank rank 0 n ...