字尾樹 字尾陣列

2021-04-17 18:39:05 字數 3056 閱讀 8828

在字串處理當中,字尾樹和字尾陣列都是非常有力的工具,其中字尾樹大家了解得比較多,關於字尾陣列則很少見於國內的資料。其實字尾陣列是字尾樹的乙個非 常精巧的替代品,它比字尾樹容易程式設計實現,能夠實現字尾樹的很多功能而時間複雜度也不太遜色,並且,它比字尾樹所占用的空間小很多。可以說,在資訊學競賽 中字尾陣列比字尾樹要更為實用。因此在本文中筆者想介紹一下字尾陣列的基本概念、構造方法,以及配合字尾陣列的最長公共字首陣列的構造方法,最後結合一些 例子談談字尾陣列的應用。

基本概念

首先明確一些必要的定義:

字符集 乙個字符集∑是乙個建立了全序關係的集合,也就是說,∑中的任意兩個不同的元素α和β都可以比較大小,要麼α<β,要麼β<α(也就是α>β)。字符集∑中的元素稱為字元。

字串 乙個字串s是將n個字元順次排列形成的陣列,n稱為s的長度,表示為len(s)。s的第i個字元表示為s[i]。

子串 字串s的子串s[i..j],i≤j,表示s串中從i到j這一段,也就是順次排列s[i],s[i+1],...,s[j]形成的字串。

字尾 字尾是指從某個位置i開始到整個串末尾結束的乙個特殊子串。字串s的從i開頭的字尾表示為suffix(s,i),也就是suffix(s,i)=s[i..len(s)]。

關 於字串的大小比較,是指通常所說的「字典順序」比較,也就是對於兩個字串u、v,令i從1開始順次比較u[i]和v[i],如果相等則令i加1,否則 若u[i]v[i]則認為u>v(也就是vlen (u)或者i>len(v)仍比較出結果,那麼若len(u)len(v)則u>v。

從字串的大小比較的定義來看,s的兩個開頭位置不同的字尾u和v進行比較的結果不可能是相等,因為u=v的必要條件len(u)=len(v)在這裡不可能滿足。

下 面我們約定乙個字符集∑和乙個字串s,設len(s)=n,且s[n]='$',也就是說s以乙個特殊字元'$'結尾,並且'$'小於∑中的任何乙個字 符。除了s[n]之外,s中的其他字元都屬於∑。對於約定的字串s,從位置i開頭的字尾直接寫成suffix(i),省去引數s。

後 綴陣列 字尾陣列sa是乙個一維陣列,它儲存1..n的某個排列sa[1],sa[2],...sa[n],並且保證 suffix(sa[i])n或者j+k>n的時候suffix(i+k)或suffix(j+k)是無明確定義的表示式,但實際上不需要考慮 這個問題,因為此時suffix(i)或者suffix(j)的長度不超過k,也就是說它們的k-字首以'$'結尾,於是k-字首比較的結果不可能相等, 也就是說前k個字元已經能夠比出大小,後面的表示式自然可以忽略,這也就看出我們規定s以'$'結尾的特殊用處了。

定義k-字尾陣列 sak儲存1..n的某個排列sak[1],sak[2],…sak[n]使得suffix(sak[i]) ≤ksuffix(sak[i+1]),1≤ij時可交換i,j,i=j時可以直接輸出結果n-sa[i]+1。

直接根據定義,用順次比較對應字元的方法來計算lcp(i,j)顯然是很低效的,時間複雜度為o(n),所以我們必須進行適當的預處理以降低每次計算lcp的複雜度。

經過仔細分析,我們發現lcp函式有乙個非常好的性質:

設ip,則

u[1]=w[1],u[2]=w[2],...u[q]=w[q]。

而min=p說明u[p+1]≠v[p+1]或v[p+1]≠w[q+1],

設u[p+1]=x,v[p+1]=y,w[p+1]=z,顯然有x≤y≤z,又由pp不成立,即lcp(i,k)≤p。 (2)

綜合(1),(2)知 lcp(i,k)=p=min,lcp lemma得證。

於是lcp theorem可以證明如下:

當j-i=1和j-i=2時,顯然成立。

設j-i=m時lcp theorem成立,當j-i=m+1時,

由lcp lemma知lcp(i,j)=min,

因j-(i+1)≤m,lcp(i+1,j)=min,故當j-i=m+1時,仍有

lcp(i,j)=min}=min|i+1≤k≤j)

根據數學歸納法,lcp theorem成立。

根據lcp theorem得出必然的乙個推論:

lcp corollary 對i≤j1且rank[i]>1,一定有h[i]≥h[i-1]-1。

為了證明性質3,我們有必要明確兩個事實:

設i1,則成立以下兩點:

fact 1 suffix(i)1說明suffix(i)和suffix(j)的第乙個字元是相同 的,設它為α,則suffix(i)相當於α後連線suffix(i+1),suffix(j)相當於α後連線suffix(j+1)。比較suffix (i)和suffix(j)時,第乙個字元α是一定相等的,於是後面就等價於比較suffix(i)和suffix(j),因此fact 1成立。fact 2可類似證明。

於是可以證明性質3:

當h[i-1]≤1時,結論顯然成立,因h[i]≥0≥h[i-1]-1。

當h[i-1]>1時,也即height[rank[i-1]]>1,可見rank[i-1]>1,因height[1]=0。

令j=i-1,k=sa[rank[j]-1]。顯然有suffix(k)1和suffix(k)1,rank[i]>1,h[i-1]>1,根據性質3,suffix(i)和suffix(rank[i]-1)至少有 前h[i-1]-1個字元是相同的,於是字元比較可以從h[i-1]開始,直到某個字元不相同,由此計算出h[i]。字元比較次數為h[i]-h[i- 1]+2。

設sa[1]=p,那麼不難看出總的字元比較次數不超過

也就是說,整個演算法的複雜度為o(n)。

求出了h陣列,根據關係式height[i]=h[sa[i]]可以在o(n)時間內求出height陣列,於是

可以在o(n)時間內求出height陣列。

結合rmq方法,在o(n)時間和空間進行預處理之後就能做到在常數時間內計算出對任意(i,j)計算出lcp(i,j)。

因為lcp(suffix(i),suffix(j))=lcp(rank[i],rank[j]),所以我們也就可以在常數時間內求出s的任何兩個字尾之間的最長公共字首。這正是字尾陣列能強有力地處理很多字串問題的重要原因之一。

字尾陣列的應用

下面結合兩個例子談談如何運用字尾陣列。

例一 多模式串的模式匹配問題

字尾陣列與字尾樹的比較

字尾樹 字尾陣列

我們考慮將乙個串的所有字尾插入乙個trie中,得到的trie就是字尾trie。我們可以發現,樹上有分叉或者是字尾節點的點的個數是o l en o len o len 個,這個後面解釋,於是把沒有分支並且不是字尾節點的點壓縮到一起,就變成了字尾樹。不難發現,字尾樹可以表示該字串的所有子串。下面分析一下...

字尾樹與字尾陣列

字尾樹和字尾陣列是字串處理的兩大神器,幾乎可處理掉一切的字串處理問題,但是在實際中,字尾陣列比字尾樹更好寫 好調,同時時間上也不差 常數很小 所以字尾陣列絕對是oi競賽之必備神器。字尾樹,實際上就是一棵字典樹。考慮將某個串 s 的所有字尾插到一棵trie裡,那麼我們就得到了一棵字尾樹。在這裡,我們不...

字尾陣列入門,字尾陣列模板整理

我自己懶得寫,就是想寫個部落格儲存下大佬的部落格位址 點這模板題 大佬的模板 include include include include include include include include include include include define inf 0x3f3f3f3f d...