字尾樹與字尾陣列

2021-07-16 07:39:19 字數 3149 閱讀 1625

字尾樹和字尾陣列是字串處理的兩大神器,幾乎可處理掉一切的字串處理問題,但是在實際中,字尾陣列比字尾樹更好寫、好調,同時時間上也不差(常數很小),所以字尾陣列絕對是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 的字尾樹。這樣的話我們...