子串:從原串中選取連續的一段,即子串
空串也是子串
字尾:suf(k)為s(k…n)構成的子串
任何子串都是某個字尾的字首
最長公共字首lcp(suf(i),suf(j))
將所有字尾suf(1),suf(2),suf(n)按照字典序從小到大排序
暴力sortn2 logn
二分+hash:
nlog2n
cmp函式中二分suf(i)和suf(j)的lcp
return s[i+|lcp|] < s[j+|lcp|]
------- 以上為暴力做法
進入正文:
sa[1]排序第1的字尾的開始位置
rank[i]=字尾suf(i)的排名
rank[sa[l]] = l
sa[rank[i]] = i
求sa然後得到rank
倍增
sub[i][k]:s從i開始長度為 2k的子串
sub[i][k] = s[i…i+(1
sa[1][k] :在長度2k的所有子串中排名第1的子串的開始位置
step 1:先求sub[1][0],sub[2][0],…,sub[n][0]的字典排序
先求長度為1的子串,然後看字典序是多少
再求2,4,8,n,
當子串長度2k>=n時,子串排序就是字尾排序
利用rank[i…n][k],如何求出rank[1…n][k+1]
二分比較,
對於兩個子串sub[i][k+1]與sub[j][k+1]比較
先比較rank[i][k]與rank[j] [ k ] (先比前半部分)
如果相等再比較rank[i+2k]與rank [ j+2k ] (比較後半部分)
相當於二元組(第一關鍵字–>rank[i][k],第二–>rank[i+2k][k])排序
寫sa時用cnt陣列實現
將a[i]陣列(1~n)基數排序,結果存放在sa陣列中
sa[1]:排名第1th的數在a中的下標
for
(int i=
1;i<=n;i++
)cnt[a[i]++;
//桶排
for(
int i=
1;i<=n;i++
)cnt[i]
+=cnt[i-1]
;//求字首和
大致過程:
for k = 1 ~ logn
按rank[i+2k][k]基數排序(第二關鍵字)
按照rank[i][k]基數排序,(第一關鍵字)
得到sa[i][k+1]陣列
由sa[i][k+1]求出rank[i][k+1]
動畫鏈結
資料結構和演算法動態視覺化 (chinese)
sa—>rank
rk[i]中有並列
//由sa得到新的rank陣列}}
for(i =
1; i <= n;
++i)
printf
("%d "
, sa[i]);
return0;
}這個**會超時,經過優化後:
#
include
#include
#include
#include
using
namespace std;
const
int n =
1000010
;char s[n]
;int n, sa[n]
, rk[n]
, oldrk[n <<1]
, id[n]
, px[n]
, cnt[n]
;// px[i] = rk[id[i]](用於排序的陣列所以叫 px)
bool
cmp(
int x,
int y,
int w)
intmain()
for(i =
1; i <= n;
++i)
printf
("%d "
, sa[i]);
return0;
}
要求全文背誦複雜度為o(n logn) 字尾陣列 最詳細講解
我們先看幾條定義 在字串s中,取任意i j,那麼在s中擷取從i到j的這一段就叫做s的乙個子串 字尾就是從字串的某個位置i到字串末尾的子串,我們定義以s的第i個字元為第乙個元素的字尾為suff i 把s的每個字尾按照字典序排序,字尾陣列sa i 就表示排名為i的字尾的起始位置的下標 而它的對映陣列rk...
字尾樹 字尾陣列
在字串處理當中,字尾樹和字尾陣列都是非常有力的工具,其中字尾樹大家了解得比較多,關於字尾陣列則很少見於國內的資料。其實字尾陣列是字尾樹的乙個非 常精巧的替代品,它比字尾樹容易程式設計實現,能夠實現字尾樹的很多功能而時間複雜度也不太遜色,並且,它比字尾樹所占用的空間小很多。可以說,在資訊學競賽 中字尾...
字尾樹 字尾陣列
我們考慮將乙個串的所有字尾插入乙個trie中,得到的trie就是字尾trie。我們可以發現,樹上有分叉或者是字尾節點的點的個數是o l en o len o len 個,這個後面解釋,於是把沒有分支並且不是字尾節點的點壓縮到一起,就變成了字尾樹。不難發現,字尾樹可以表示該字串的所有子串。下面分析一下...