http://chencb.ycool.com/post.1901840.html
今天把后綴數組給看了下,評價就一句話:很好,很強大
懶得寫廢話,直接把相關重點簡單記錄一下:后綴數組就是將字符串所有后綴排序后的數組,設字符串為S,令后綴Suffix(i)表示S[i..len(S)]。用兩個數組記錄所有后綴的排序結果:
- Rank[i]記錄Suffix(i)排序后的序號,即Suffix[i]在所有后綴中是第Rank[i]小的后綴
- SA[i]記錄第i位后綴的首字母位置,即Suffix[SA[i]]在所有后綴中是第i小的后綴
然后就是怎么快速求所有后綴的順序了,其中的關鍵是如何減少兩個后綴比較的復雜度
方法是倍增法,定義一個字符串的k-前綴為該字符串的前k個字符組成的串,關於在k-后綴上的定義Suffix(k,i)、SA[k,i]和Rank[k,i]類似於前,則有
- 若Rank[k,i]=Rank[k,j]且Rank[k,i+k]=Rank[k,j+k],則Suffix[2k,i]=Suffix[2k,j]
- 若Rank[k,i]=Rank[k,j]且Rank[k,i+k]<Rank[k,j+k],則Suffix[2k,i]<Suffix[2k,j]
- 若Rank[k,i]<Rank[k,j],則Suffix[2k,i]<Suffix[2k,j]
這樣就能在常數時間內比較Suffix(2^k, i)之間的大小,從而對Suffix(2^k,i)時行排序,最后當2^k>n時,Suffix(2^k, i)之間的大小即為所有后綴之間的大小
於是求出了所有后綴的排序,有什么用呢?主要是用於求它們之間的最長公共前綴(Longest Common Prefix,LCP)
令LCP(i,j)為第i小的后綴和第j小的后綴(也就是Suffix(SA[i])和Suffix(SA[j]))的最長公共前綴的長度,則有如下兩個性質:
- 對任意i<=k<=j,有LCP(i,j) = min(LCP(i,k),LCP(k,j))
- LCP(i,j)=min(i<k<=j)(LCP(k-1,k))
第一個性質是顯然的,它的意義在於可以用來證明第二個性質。第二個性質的意義在於提供了一個將LCP問題轉換為RMQ問題的方法:
令height[i]=LCP(i-1,i),即height[i]代表第i小的后綴與第i-1小的后綴的LCP,則求LCP(i,j)就等於求height[i+1]~height[j]之間的RMQ,套用RMQ算法就可以了,復雜度是預處理O(nlogn),查詢O(1)
然后height的求法要用到另一個數組:令h[i]=height[SA[i]],即h[i]表示Suffix(i)的height值(同時height[i]就表示Suffix(SA[i])的height值),則有height[i]=h[Rank[i]]
然后h[i]有個性質:
- h[i] >= h[i-1]-1
用這個性質我們在計算h[i]的時候進行后綴比較時只需從第h[i-1]位起比較,從而總的比較的復雜度是O(n),也就是說h數組在O(n)的時間內解決了。h解決了height也解決了,從而整個LCP問題就解決了^_^
然后后綴數組的應用就是利用它的LCP在需要字符串比較時降低復雜度。同時由於后綴數組的有序性可以很方便地使用二分
於是總結一下要點:
- 利用倍增算法在O(nlogn)的時間內對后綴數組進行排序
- 利用h數組的性質在O(n)的時間內求出排序后相鄰后綴間的LCP數組height
- 利用LCP的性質將平凡LCP問題轉化為height數組上的RMQ問題