主要是基於基數排序,如果基數排序沒弄懂**就會很難理解:
首先從k=0開始,從字尾陣列裡面選取步長為2^k的字尾陣列的前子串
然後進行基數排序
如果排序後所有的名次陣列的值都不相同,那麼排序結束;
否則,k++(也就是步長翻倍),繼續排序。
陣列sa(sorted array):構造完成前表示關鍵字陣列,下標表示名次,值表示關鍵字的首字元位置,值相同的時候名次根據在原串中相對位置的先後決定;構造完成後表示字尾陣列,下標表示名次,值表示字尾的首字元位置。 比如'abb'的sa: sa[0] = 0 sa[1] = 2 sa[2] = 1
陣列x:表示rank陣列,下標表示關鍵字的位置,值表示關鍵字大小(rank),相同的值有相同的rank。初始化為字串r的每個字元大小(此時x並不代表rank,只借助其值比較相對大小)。在每次迭代後,根據sa重新賦值,並代表rank。
陣列y:排序後的第二關鍵字陣列,下標表示名次,值代表第二關鍵字的首字元位置。也就是類似於第二關鍵字的sa陣列。
注意的是右圖中的y不是**中的y陣列,右圖的y只是演示表示第二關鍵字的排名。
for(i = 0; i < m; i++)
ws[i] = 0;
for(i = 0; i < n; i++)
ws[x[i] = r[i]]++;
for(i = 1; i < m; i++) //分為128個桶來進行基數排序
ws[i] += ws[i-1];
for(i = n-1; i >= 0; i--)
sa[--ws[x[i]]] = i;
複製**
上面**的意思是,首先我們對陣列初始化,m初始的時候我們簡單採用asicii碼的m=128。要對長度為1的字串進行排序,這樣就求出了乙個sa。
for(j=1,p=1;pfor(p=0,i=n-j;ifor(i=0;iif(sa[i]>=j) y[p++]=sa[i]-j;
for(i=0;ifor(i=0;ifor(i=0;ifor(i=1;ifor(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i複製**
這段**非常精妙,也很難理解。 接下來我們從步長為1開始逐步倍增步長來進行基數排序,基數排序要分兩次,第一次是對第二關鍵字排序,第二次是對第一關鍵字排序。對第二關鍵字排序的結果實際上可以利用上一次求得的sa直接算出,沒有必要再算一次。那麼怎麼算呢?
這一段也不好理解,目的呢是生成rank陣列x。但是如果我們按照sa的方式簡單對映,x[i]肯定是彼此不相同的。但是實際上當前步長下有可能他們的rank是相同的(圖中的例子是不存在的,因為才排了一次rank就彼此不相同了)。 這裡由於y陣列實際上已經沒用了,為了節省空間,我們交換x,y用y來儲存rank值。
那麼怎麼判定重複? int cmp(int *r , int a, int b, int l) 這段也需要仔細**下,我們直到最直接的辦法是相鄰的兩個字尾陣列從字尾陣列的頭一直比較到尾部,但是這裡只比較了第一關鍵字和第二關鍵字對應的字元就可以了,這是為什麼?因為我們直到假設當前步長是2^k,第一,二個關鍵字分別相當於前後2^(k-1)個字元的名次,名次如果都相同那整個字串肯定都相同了.
並且,如果r[a]=r[b],說明以r[a]或r[b]開頭的長度為l的字串肯定不包括字元r[n-1](否則,那麼他們的長度肯定不同,r[a],r[b]這兩個名次必然不一樣),所以呼叫變數r[a+l]和r[b+l]不會導致陣列下標越界,這樣就不需要做特殊判斷。(這點也真的很妙)
還有兩個細節,for(j=1,p=1;p=2,m=p)
每次基數排序是o(n),排序的次數決定於最長公共子串的長度,最壞情況需要迴圈logn次,所以總的複雜度為o(nlogn)。
空間上,引入了第二關鍵字排序陣列y,rank陣列x,計數器ws,中間陣列wv。加上原本需要的r與sa以及對字串串陣列轉換成的int陣列,一共是7n,空間要求為 o(n).
#include
#include
#define maxn 100
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)
}//測試**
int main
() da(r,sa,len,128);
for(int i=0;iprintf("%d\t%d\t",i,sa[i]);
for(int j=sa[i];jprintf("%c",r_str[j]);
printf("\n");
}}複製**
字尾陣列倍增法
字尾陣列 字尾陣列是處理字串的有力工具。字尾陣列是字尾樹的乙個非常精巧的替代品,它比字尾樹容易程式設計實現,能夠實現字尾樹的很多功能而時間複雜度也並不遜色,而且它比字尾樹所占用的記憶體空間小很多。可以說,在資訊學競賽中字尾陣列比字尾樹要更為實用。1.1 基本定義 子串 字串s的子串r i.j i j...
字尾陣列(倍增法)
字尾陣列 suffix array 將某個字串的所有字尾按字典序排序後得到的陣列。演算法 樸素實現 直接將所有字尾進行排序,將n個長度為o n 的字串進行排序,時間複雜度o n 2 logn 倍增演算法 通過充分利用各個字尾之間的聯絡,將構造字尾陣列的最壞時間複雜度成功降至o n logn 倍增法實...
字尾陣列倍增演算法模板詳解
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 ...