原理
其本質就是把字串的所有字尾進行排序。
用普通排序需要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 對於二元組的基數排序應該是這樣做的 首先把所...