字尾樹和字尾陣列是字串處理的兩大神器,幾乎可處理掉一切的字串處理問題,但是在實際中,字尾陣列比字尾樹更好寫、好調,同時時間上也不差(常數很小),所以字尾陣列絕對是oi競賽之必備神器。字尾樹,實際上就是一棵字典樹。考慮將某個串
s 的所有字尾插到一棵trie裡,那麼我們就得到了一棵字尾樹。在這裡,我們不會講字尾樹的構造,只會略微講一點其的思想。
在字尾樹而言,匹配就變成一件易事了。考慮乙個字串匹配問題,如果我們要在乙個串
a中找很多個字串b=
b0,b
1,b2
… ,那麼也只需建好
a 的字尾樹,然後直接放就好了……
另外,還有許多擴充套件應用,但在這裡我就不細講了。下面來講字尾陣列。
同樣將乙個字串
s的所有字尾,按照字典序排序,那麼我們就得到了字尾陣列。也就是說,字尾陣列的第
i 個正是字典序第
i大的字尾的編號。
好的,那麼怎麼構造呢?一種方法是從排序入手,由於字串的比較是θ(
n)的,所以說再乘上排序的比較次數(如使用快排)
nlog2n
,那麼總的時間複雜度就是o(
n2log2n)
。 但是,字尾有一些特殊的性質,可以幫我們優化排序。
我們要用的方法,是倍增演算法,如果,我們每一次比較的時候,並不比較整個字尾,而是每次只比較一部分。
首先,我們將每個字尾的第乙個字元排序,做出第一輪的sa(也就是suffix array,字尾陣列),然後排第二輪,這次排兩個字元,相當於是給二元組排序。
然後,我們開始排四個字元,注意到,由於4=
2+2 ,所以實際上……我們如果利用上一輪的排名,比如我們搞出另乙個ra
nk,使ra
nk(i
) 為第
i 個字尾的排名(可能重疊),然後,假設我們現在要排字尾
j的前四個字元,那麼我們將這四個字元拆開,我們就可以得到前面的兩個字元和後面的兩個字元。前面的兩個字元的排名正好是ra
nk(j
) ,而後面的兩個字元,則是字尾j+
2 的前兩個字元,其的排名是ra
nk(j
+2) 。另外,我們來順便證明乙個定理:將若干個長度相同(設都為
k )的字串中的乙個(設為
e)分成兩段,長度分別為
l 和|e
|−l,那麼如果我們已知字串
e 在所有字串的前
l的字元構成的串中的排名和剩下來|e
|−l 個字元構成的串中的排名r1
和r2 ,那麼整乙個字串的排名等價於二元組(r
1,r2
) 的排名。證明很容易,我們只需要注意到兩個二元組(a
,b) 和(c
,d) 的比較規則是當a≠
c 時比較
a 和
c,而當a=
c 時比較
b 和
d,那麼我們就能夠看出,由於我們已經有了前面一段的排名,那麼我們只需要比較排名,排名哪乙個更前,哪乙個的字典序就相應的越高;如果其前半段的排名相等,即字典序相等,那麼其前半段是完全一樣的,我們就可以忽略前半段,比較後半段的排名。仔細一想,我們就可以發現,上面給出的二元組的比較過程,和字典序的比較過程,是完全一樣的!只不過,我們「已經比較」過某些東西了,所以不用再直接地比較兩兩間的字典序了。
利用倍增演算法和快速排序的結合,我們可以在
log2n×
θ(nlog2n
)=θ(
nlog22
n)的時間內解決這個問題,**也很短,五六行左右就好了。如果沒有時間寫其他的話,可以直接用so
rt()
和倍增演算法相對暴力地解決。
但是,我們有更高效的演算法。基數排序!!!
在這裡,我們可以先按照上一輪的結果將第二個關鍵字排成乙個序(相同的不管它),然後按照這個順序,桶排。注意,插入的順序應當和我們預先排的順序一樣。也就是說,大概是下面這樣:
for (int i=0;i然後我們再將放進去的二元組再按照桶的大小順序取出來,放好順序,基數排序就完成了。
但是,光有構造遠遠不夠,這樣我們只能擁有兩個陣列(而且本質上相同)ra
nk和s
a 。我們還需要乙個特殊的工具,這個工具叫做最長公共字首。
兩個字串s1
和s2 的最長公共字首lc
p(s1
,s2)
的定義為lc
p(s1
,s2)
=maxi=
1min
那麼,我們可以很容易地發現,其實某兩個字尾的最長公共字首lc
p(ss
ai,s
saj)
的值,實際上是
mink
=min
max−1l
cp(s
k,sk
+1) 。證明很容易,首先顯然地,
mink
=min
max−1l
cp(s
k,sk
+1)≤
lcp(
ssai
,ssa
j)(相當於若干個公共字首的公共部分,可能可以再長),另外若設p=
mink
=min
max−1l
cp(s
k,sk
+1) ,q=
lcp(
ssai
,ssa
j),而
q>
p ,那麼可以推出矛盾,這裡不證(篇幅太長了,公式太大了……)
好吧,總之這個被證出來了,那麼我們怎樣快速地求出lc
p(ss
ai,s
sai+
1)呢?容易證明,lc
p(ss
ai,s
sai+
1)≥l
cp(s
sai,
ssai
−1)−
1 字尾陣列的應用相當多,而且實現相對簡潔(可能吧),只要多想想,幾乎沒什麼是搞不定的……
【p.s.】字尾陣列還可以有各種擴充套件,比如字尾家族的各種資料結構:字尾樹、字尾陣列、字尾自動機、字尾仙人掌……所以,太多了……
字尾樹 字尾陣列
在字串處理當中,字尾樹和字尾陣列都是非常有力的工具,其中字尾樹大家了解得比較多,關於字尾陣列則很少見於國內的資料。其實字尾陣列是字尾樹的乙個非 常精巧的替代品,它比字尾樹容易程式設計實現,能夠實現字尾樹的很多功能而時間複雜度也不太遜色,並且,它比字尾樹所占用的空間小很多。可以說,在資訊學競賽 中字尾...
字尾樹 字尾陣列
我們考慮將乙個串的所有字尾插入乙個trie中,得到的trie就是字尾trie。我們可以發現,樹上有分叉或者是字尾節點的點的個數是o l en o len o len 個,這個後面解釋,於是把沒有分支並且不是字尾節點的點壓縮到一起,就變成了字尾樹。不難發現,字尾樹可以表示該字串的所有子串。下面分析一下...
字尾樹與字尾自動機
先學習的是字尾樹向的字尾自動機qaq 介紹字尾樹的定義 把所有字尾插到trie裡 然而實際上是插到trie裡之後再壓縮掉一些邊,這樣就是字尾樹了。但我們發現如果乙個乙個找的話複雜度是o n2 我們考慮倒著建立字尾樹,即 串s a s 那麼我們先建立s 的字尾樹,然後建立a s 的字尾樹。這樣的話我們...