1. 程式人生 > >字尾陣列處理多字串公共子串總結

字尾陣列處理多字串公共子串總結

個人經驗:對單個字串問題求個數需要列舉,求長度可以利用二分

公共子串:如果字串L同時出現在字串A和字串B中,則稱字串L是字串A和字串B的公共子串。

與子序列不同的是,子序列可以斷續,通常用dp解決,子串要求連續。

最長公共子串問題的字尾陣列解法

回顧一下字尾陣列,SA[i]表示排名第i的字尾的位置,Height[i]表示字尾SA[i]和SA[i-1]的最長公共字首(Longest Common Prefix,LCP),簡記為Height[i]=LCP(SA[i],SA[i-1])。連續的一段字尾SA[i..j]的最長公共字首,就是H[i-1..j]的最小值,即LCP(SA[i..j])=Min(H[i-1..j])。

求N個串的最長公共子串,可以轉化為求一些字尾的最長公共字首的最大值,這些字尾應分屬於N個串。具體方法如下:

設N個串分別為S1,S2,S3,...,SN,首先建立一個串S,把這N個串用不同的分隔符連線起來。S=S1[P1]S2[P2]S3...SN-1[PN-1]SN,P1,P2,...PN-1應為不同的N-1個不在字符集中的字元,作為分隔符(後面會解釋為什麼)。

接下來,求出字串S的字尾陣列和Height陣列,可以用倍增演算法,或DC3演算法。

然後二分列舉答案A,假設N個串可以有長度為A的公共字串,並對A的可行性進行驗證。如果驗證A可行,A'(A'<A)也一定可行,嘗試增大A,反之嘗試縮小A。最終可以取得A的最大可行值,就是這N個串的最長公共子串的長度。可以證明,嘗試次數是O(logL)的。

於是問題就集中到了,如何驗證給定的長度A是否為可行解。方法是,找出在Height陣列中找出連續的一段Height[i..j],使得i<=k<=j均滿足Height[k]>=A,並且i-1<=k<=j中,SA[k]分屬於原有N個串S1..SN。如果能找到這樣的一段,那麼A就是可行解,否則A不是可行解。

具體查詢i..j時,可以先從前到後列舉i的位置,如果發現Height[i]>=A,則開始從i向後列舉j的位置,直到找到了Height[j+1]<A,判斷[i..j]這個區間內SA是否分屬於S1..SN。如果滿足,則A為可行解,然後直接返回,否則令i=j+1繼續向後列舉。S中每個字元被訪問了O(1)次,S的長度為NL+N-1,所以驗證的時間複雜度為O(N

L)。

到這裡,我們就可以理解為什麼分隔符P1..PN-1必須是不同的N-1個不在字符集中的字元了,因為這樣才能保證S的字尾的公共字首不會跨出一個原有串的範圍。(否則下一個字串的部分字首也會成為公共的部分)

題目小結

兩個字串

多個字串