字尾陣列SA

2021-09-29 10:27:31 字數 4023 閱讀 6718

原理

其本質就是把字串的所有字尾進行排序。

用普通排序需要o(nlogn),但是字串比較和數字比較不同,所以實際需要o(n*nlogn)。

為了讓這個過程快一點,所以有了倍增演算法,o(nlogn),和dc3演算法,o(n)。

倍增演算法比較簡單,也比較好寫,具體可以參考這個大佬的部落格。

dc3演算法複雜一點,但是快一些,主要步驟如下,以字串yabbadabbado舉例:

把下標0—n-1分成兩類,第一類為i mod 3 = 1或者i mod 3 = 2, 第二類為i mod 3=0。

三個連續的字元稱為乙個三元組,把第一類中每個下標都擴充成乙個以它為開頭三元組。上述就能得到[abb][ada][bba][do$ ][bba][dab ][bad][o$$],然後用基數排序進行排序。如果存在名次相同的就遞迴執行dc3演算法,最終能得到第一類下標的排名。

通過第一類下標的排名得到第二類下標的排名,第二類下標對應的字尾就是在i mod 3=1的字尾前前加了個字元,從i開始的字尾的排名已經知道了,所以只要比這個多的字元就行了。

現在兩個類的下標都是有序的了,歸併起來就好了,具體比較的過程可以分為第二類和第一類中的i mod 3=1比較以及第二類和第一類中的i mod 3=2比較。

實現

倍增演算法

p3809 【模板】字尾排序

#include

#include

using

namespace std;

const

int maxn =

1000010

;int rk[maxn]

, tp[maxn]

, sa[maxn]

, tax[maxn]

;// 關鍵字 當前的下標排序

char st[maxn]

;int m, n;

inline

void

rsort()

inline

void

suffix()

rsort()

;swap

(rk, tp)

; rk[sa[1]

]=1;

num =1;

for(

int i =

2; i <= n;

++i)

if(num == n)

break

; m = num;}}

intmain()

dc3

#include

#include

using

namespace std;

//從壓縮後的下標恢復最原始下標,壓縮方法見後面。

#define getrealpos()(sa12[pos12]inline

bool

cmp(

int a1,

int a2,

int b1,

int b2)

inline

bool

cmp(

int a1,

int a2,

int a3,

int b1,

int b2,

int b3)

void

radixsort

(int

* oldidx,

int* newidx,

int*origin,

int n,

int upperbound)

for(

int i =

0; i < n;

++i)

newidx[cnt[origin[oldidx[i]]]

++]= oldidx[i]

;delete

cnt;

}void

suffixarray

(int

*st,

int*sa,

int n,

int upperbound)

//對第一類下標進行排名。

radixsort

(s12, sa12, st +

2, n12, upperbound)

;radixsort

(sa12, s12, st +

1, n12, upperbound)

;radixsort

(s12, sa12, st, n12, upperbound)

;int cnt =

0, pre0 =-1

, pre1 =-1

, pre2 =-1

;for

(int i =

0; i < n12;

++i)

//為了壓縮空間,相當於壓縮了1/3的空間。

if(sa12[i]%3

==1) s12[sa12[i]/3

]= cnt;

else s12[sa12[i]/3

+ n0]

= cnt;

}//如果存在相同的排名,就遞迴

if(cnt < n12)

else

//對第二類下標進行排名

for(

int i =

0, j =

0; i < n12;

++i)

radixsort

(s0, sa0, st, n0, upperbound)

;for

(int pos0 =

0, pos12 = n0 - n1, k =

0; k < n;

++k)

else

}delete

sa12;

delete

sa0;

delete

s0;delete

s12;

}int

main()

;int sa[15]

;suffixarray

(s, sa,7,

128)

;return0;

}

void

get_height()

}

題目

p2408 不同子串個數

所有字串的個數為(1+n)* n/2,只要減去那些重複的字串就能得到答案了。

每個子串都是某個字尾的字首,所以可以考慮n個字尾,每個字尾最多能新加入與字尾長度相同的子串,如果字尾i和字尾i-1的lca>0的話,那就說明字尾i的前lca個子串已經被加入過了,要減掉。

#include

#include

using

namespace std;

const

int maxn =

100010

;int rk[maxn]

, tp[maxn]

, sa[maxn]

, tax[maxn]

, height[maxn]

;char st[maxn]

;long

long

int m, n;

inline

void

rsort()

inline

void

suffix()

rsort()

;swap

(rk, tp)

; rk[sa[1]

]=1;

num =1;

for(

int i =

2; i <= n;

++i)

if(num == n)

break

; m = num;}}

void

get_height()

}int

main()

SA 字尾陣列

首先一定要確定sa 是個什麼東西 sa i 表示的是排名為 i 的字尾是哪乙個 至於字尾 i的排名是多少,那個是ra nk i 當然啦 最最最難懂的就是基數排序 要是不用基數排序,每次對於乙個二元組直接so rt一下 這樣的複雜度是o nlog 2 對於二元組的基數排序應該是這樣做的 首先把所有元素...

字尾陣列SA

給定乙個字串s,按字典序排序s的所有子串 鬼知道什麼思想,好像沒有什麼思想。哦,想起來了,是倍增。考慮最簡單的字尾間o n o n 比較和快排o nlog n o n logn 總複雜度o n2lo gn o n 2log n 考慮優化字串間的比較,用倍增的思想,假設k 2 k 2 長度的已經比完了...

SA 字尾陣列

首先一定要確定 sa 是個什麼東西 sa i 表示的是排名為 i 的字尾是哪乙個 至於字尾 i 的排名是多少,那個是 rank i 當然啦最最最難懂的就是基數排序 要是不用基數排序,每次對於乙個二元組直接 sort 一下 這樣的複雜度是 o nlog 2 對於二元組的基數排序應該是這樣做的 首先把所...