標籤:自己 ref 字元 遇到 blog 編程 rmq href 找不到
之前覺得尾碼自動機會了,就忽略了尾碼數組,現在發現尾碼數組+二分的功能很強,而且尾碼自動機好像實現不了。
轉寄一下,方便隊友大概看一下。這幾天我也儘快惡補一下。
(找不到原博主網站了,失誤)
尾碼數組解題總結:1、求單個子串的不重複子串個數。SPOJ 694、SPOJ 705.
這個問題是一個特殊求值問題。要認識到這樣一個事實:一個字串中的所有子串都必然是它的尾碼的首碼。(這句話稍微有點繞...)對於每一個sa[i]尾碼,它的起始位置sa[i],那麼它最多能得到該尾碼長度個子串(n-sa[i]個),而其中有height[i]個是與前一個尾碼相同的,所以它能產生的實際尾碼個數便是n-sa[i]-height[i]。遍曆一次所有的尾碼,將它產生的尾碼數加起來便是答案。
代碼及題解:http://hi.baidu.com/fhnstephen/blog/item/68f919f849748668024f56fb.html
2、尾碼的最長公用首碼。(記為lcp(x,y))
這是height數組的最基本性質之一。具體的可以參看羅穗騫的論文。尾碼i和尾碼j的最長公用首碼的長度為它們在sa數組中所在排位之間的height值中的最小值。這個描述可能有點亂,正規的說,令x=rank[i],y=rank[j],x<y,那麼lcp(i,j)=min(height[x+1],height[x+2]...height[y])。lcp(i,i)=n-sa[i]。解決這個問題,用RMQ的ST演算法即可(我只會這個,或者用最近公用祖先那個轉化的做法)。
3、最長重複子串(可重疊)
要看到,任何一個重複子串,都必然是某兩個尾碼的最長公用首碼。因為,兩個尾碼的公用首碼,它出現在這兩個尾碼中,並且起始位置時不同的,所以這個公用首碼必然重複出現兩次以上(可重疊)。而任何兩個尾碼的最長公用首碼為某一段height值中的最小值,所以最大為height值中的最大值(即某個lcp(sa[i],sa[i+1]))。所以只要算出height數組,然後輸出最大值就可以了。
一道題目和代碼:http://hi.baidu.com/fhnstephen/blog/item/4ed09dffdec0a78eb801a0ba.html
4、最長重複不重疊子串 PKU1743
這個問題和3的唯一區別在於能否重疊。加上不能重疊這個限制後,直接求解比較困難,所以我們選擇二分枚舉答案,將問題轉換為判定性問題。假設當時枚舉的長度為k,那麼要怎樣判斷是否存在長度為k的重複不重疊子串呢?
首先,根據height數組,將尾碼分成若干組,使得每組尾碼中,尾碼之間的height值不小於k。這樣分組之後,不難看出,如果某組尾碼數量大於1,那麼它們之中存在一個公用首碼,其長度為它們之間的height值的最小值。而我們分組之後,每組尾碼之間height值的最小值大於等於k。所以,尾碼數大於1的分組中,有可能存在滿足題目限制條件的長度不小於k的子串。只要判斷滿足題目限制條件成立,那麼說明存在長度至少為k的合法子串。
對於本題,限制條件是不重疊,判斷的方法是,一組尾碼中,起始位置最大的尾碼的起始位置減去起始位置最小的尾碼的起始位置>=k。滿足這個條件的話,那麼這兩個尾碼的公用首碼不但出現兩次,而且出現兩次的起始位置間隔大於等於k,所以不會重疊。
深刻理解這種height分組方法以及判斷重疊與否的方法,在後面的問題中起到舉足輕重的作用。
練習及題解:http://hi.baidu.com/fhnstephen/blog/item/85a25b208263794293580759.html
5、最長的出現k次的重複(可重疊)子串。 PKU3261
使用尾碼數組解題時,遇到“最長”,除了特殊情況外(如問題3),一般需要二分答案,利用height值進行分組。本題的限制條件為出現k次。只需判斷,有沒有哪一組尾碼數量不少於k就可以了。相信有了我前面問題的分析作為基礎,這個應該不難理解。注意理解“不少於k次”而不是“等於k次”的原因。如果理解不了,可以找個具體的例子來分析分析。
題目及題解:http://hi.baidu.com/fhnstephen/blog/item/be7d15133ccbe7f0c2ce79bb.html
6、最長迴文子串 ural1297
這個問題沒有很直接的方法可以解決,但可以採用枚舉的方法。具體的就是枚舉迴文子串的中心所在位置i。注意要分迴文子串的長度為奇數還是偶數兩種情況分析。然後,我們要做的,是要求出以i為中心的迴文子串最長為多長。利用尾碼數組,可以設計出這樣一種求法:求i往後的尾碼與i往前的首碼的最長公用首碼。我這裡的表述有些問題,不過不影響理解。
要快速地求這個最長首碼,可以將原串反寫之後接在原串後面。在使用尾碼數組的題目中,串連兩個(n個)字串時,中間要用不可能會出現在原串中,不一樣的非0號的字元將它們隔開。這樣可以做到不影響尾碼數組的性質。然後,問題就可以轉化為求兩個尾碼的最長公用首碼了。具體的細節,留給大家自己思考...(懶...原諒我吧,都打這麼多字了..一個多小時了啊TOT )
題目及題解:http://hi.baidu.com/fhnstephen/blog/item/68342f1d5f9e3cf81ad576ef.html
7、求一個串最多由哪個串複製若干次得到 PKU2406
具體的問題描述請參考PKU2406.這個問題可以用KMP解決,而且效率比尾碼數組好。
利用尾碼數組直接解決本題也很困難(主要是,就算二分答案,也難以解決轉變而成的判定性問題。上題也是),但可以同過枚舉模板串的長度k(模板串指被複製的那個串)將問題變成一個尾碼數組可以解決的判定性問題。首先判斷k能否被n整除,然後只要看lcp(1,k+1)(實際在用c寫程式時是lcp(0,k))是否為n-k就可以了。
為什麼這樣就行了呢?這要充分考慮到尾碼的性質。當lcp(1,k+1)=n-k時,尾碼k+1是尾碼1(即整個字串)的一個首碼。(因為尾碼k+1的長度為n-k)那麼,尾碼1的前k個字元必然和尾碼k+1的前k個字元對應相同。而尾碼1的第k+1..2k個字元,又相當於尾碼k+1的前k個字元,所以與尾碼1的前k個字元對應相同,且和尾碼k的k+1..2k又對應相同。依次類推,只要lcp(1,k+1)=n-k,那麼s[1..k]就可以通過自複製n/k次得到整個字串。找出k的最小值,就可以得到n/k的最大值了。
題目及題解:http://hi.baidu.com/fhnstephen/blog/item/5d79f2efe1c3623127979124.html
8、求兩個字串的最長公用子串。Pku2774、Ural1517
首先區分好“最長公用子串”和“最長公用子序列”。前者的子串是連續的,後者是可以不連續的。
對於兩個字串的問題,一般情況下均將它們連起來,構造height數組。然後,最長公用子串問題等價於尾碼的最長公用首碼問題。只不過,並非所有的lcp值都能作為問題的答案。只有當兩個尾碼分屬兩個字串時,它們的lcp值才能作為答案。與問題3一樣,本題的答案必然是某個height值,因為lcp值是某段height值中的最小值。當區間長度為1時,lcp值等於某個height值。所以,本題只要掃描一遍尾碼,找出尾碼分屬兩個字串的height值中的最大值就可以了。判斷方法這裡就不說明了,留給大家自己思考...
題目及題解:
http://hi.baidu.com/fhnstephen/blog/item/8666a400cd949d7b3812bb44.html
http://hi.baidu.com/fhnstephen/blog/item/b5c7585600cadfc8b645aebe.html
9、重複次數最多的重複子串 SPOJ 687,Pku3693
難度比較大的一個問題,主要是羅穗騫的論文裡的題解寫得有點含糊不清。題目的具體含義可以去參考Pku3693.
又是一題難以通過二分枚舉答案解決的問題(因為要求的是重複次數),所以選擇樸素枚舉的方法。先枚舉重複子串的長度k,再利用尾碼數組來求長度為k的子串最多重複出現多少次。注意到一點,假如一個字串它重複出現2次(這裡不討論一次的情況,因為那是必然的),那麼它必然包含s[0],s[k],s[2*k]...之中的相鄰的兩個。所以,我們可以枚舉一個數i,然後判斷從i*k這個位置起的長度為k的字串能重複出現多少次。判斷方法和8中的相似,lcp(i*k,(i+1)*k)/k+1。但是,僅僅這樣會忽略點一些特殊情況,即重複子串的起點不在[i*k]位置上時的情況。這種情況應該怎麼求解呢?看下面這個例子:
aabababc
當k=2,i=1時,枚舉到2的位置,此時的重複子串為ba(注意第一位是0),lcp(2,4)=3,所以ba重複出現了2次。但實際上,起始位置為1的字串ab出現次數更多,為3次。我們注意到,這種情況下,lcp(2,4)=3,3不是2的整數倍。說明當前重複子串在最後沒有多重複出現一次,而重複出現了部分(這裡是多重複出現了一個b)。如果我這樣說你沒有看懂,那麼更具體地:
sa[2]=bababc
sa[4]=babc
lcp=bab
現在注意到了吧,ba重複出現了兩次之後,出現了一個b,而a沒有出現。那麼,不難想到,可以將枚舉的位置往前挪一位,這樣這個最後的b就能和前面的一個a構成一個重複子串了,而假如前挪的一位正好是a,那麼答案可以多1。所以,我們需要求出a=lcp(i*k,(i+1)*k)%n,然後向前挪k-a位,再用同樣的方法求其重複出現的長度。這裡,令b=k-a,只需要lcp(b,b+k)>=k就可以了。實際上,lcp(b,b+k)>=k時,lcp(b,b+k)必然大於等於之前求得的lcp值,而此時答案的長度只加1。沒有理解的朋友細細體會下吧。
題目及題解:http://hi.baidu.com/fhnstephen/blog/item/870da9ee3651404379f0555f.html
10.多個串的公用子串問題 PKU3294
首先將串聯接起來,然後構造height數組,然後怎麼辦呢?
對,二分答案再判斷是否可行就行了。可行條件很直觀:有一組尾碼,有超過題目要求的個數個不同的字串中的尾碼存在。即,假如題目要求要出現在至少k個串中,那麼就得有一組尾碼,在不同字串中的尾碼數大於等於k。
題目及題解:http://hi.baidu.com/fhnstephen/blog/item/49c3b7dec79ec5e377c638f1.html
11、出現或反轉後出現所有字串中的最長子串 PKU1226
http://hi.baidu.com/fhnstephen/blog/item/7fead5020a16d2da267fb5c0.html
12、不重疊地至少兩次出現在所有字串中的最長子串 spoj220http://hi.baidu.com/fhnstephen/blog/item/1dffe1dda1c98754cdbf1a35.html
之所以把兩題一起說,因為它們大同小異,方法在前面的題目均出現過。對於多個串,連起來;反轉後出現,將每個字串反寫後和原串都連起來,將反寫後的串和原串看成同一個串;求最長,二分答案後height分組;出現在所有字串中(反寫後的也行),判斷方法和10一樣,k=n而已;不重疊見問題4,只不過這裡對於每個字串都要進行檢驗而已。
13、兩個字串的重複子串個數。 Pku3415
我個人覺得頗有難度的一個問題。具體的題目描述參看Pku3415。
大家可以移步到這:http://hi.baidu.com/fhnstephen/blog/item/bf06d001de30fc034afb51c1.html
推薦大家根據我這個題解中的提示,自己思考得出本題的解法。作為本筆記的最後一個問題,我不打算說太多了。實在想不出來的,直接聯絡我吧,我的QQ:403231899,讓我知道你也是學習編程的,我就會加你了。
14、最後的總結
用尾碼數組解題有著一定的規律可循,這是尾碼的性質所決定的,具體歸納如下:
1、N個字串的問題(N>1)
方法:將它們串連起來,中間用不會出現在原串中的,互不相同的,非0號字元分隔開。
2、無限制條件下的最長公用子串(重複子串算是尾碼們的最長公用首碼)
方法:height的最大值。這裡的無限制條件是對子串無限制條件。最多隻能是兩個串的最長公用子串,才可以直接是height的最大值。
3、特殊條件下的最長子串
方法:二分答案,再根據height數組進行分組,根據條件完成判定性問題。三個或以上的字串的公用子串問題也需要二分答案。設此時要驗證的串長度為len,特殊條件有:
3.1、出現在k個串中
條件:屬於不同字串的尾碼個數不小於k。(在一組尾碼中,下面省略)
3.2、不重疊
條件:出現在同一字串中的尾碼中,出現位置的最大值減最小值大於等於len。
3.3、可重疊出現k次
條件:出現在同一字串中的尾碼個數大於等於k。若對於每個字串都需要滿足,需要逐個字串進行判斷。
4、特殊計數
方法:根據尾碼的性質,和題目的要求,通過自己的思考,看看用尾碼數組能否實現。一般和“子串”有關的題目,用尾碼數組應該是可以解決的。
5、重複問題
知道一點:lcp(i,i+k)可以判斷,以i為起點,長度為k的一個字串,它向後自複製的長度為多少,再根據具體題目具體分析,得出演算法即可。
- 單個字串問題
- 1重複子串
- 1,1 可交叉最長重複子串
- 1,2 不可交叉最長重複子串poj1743
- 1,3 可交叉的k次最長重複子串
- 2子串個數問題
- 2,1 不相同子串個數spoj694spoj705
- 3迴圈子串問題
- 3,1 求最小迴圈節
- 3,2 重複次數最多的連續重複子串spoj687poj3693
- 兩個字串串問題
- 1公用子串問題
- 1,1 最長公用子串poj2774ural1517
- 2子串個數問題
- 多個字串問題
- 1公用子串問題
- 1,1 在k個字串中的出現的最長子串poj3294
- 1,2 在每個字串中出現k次的最長子串spoj220
- 1,3 在每個字串中或反轉後出現的最長子串poj1226
【轉】尾碼數組解題總結