字串的最長回文子串 manacher演算法

2021-09-05 09:16:16 字數 2702 閱讀 9155

字串概念

回文子串概念

通常我們熟知的求解字串的最長回文字串的方法有以下兩種演算法:

1、o(n^2)列舉子串的左右兩個端點->o(n)判斷該子串是否為回文串:總複雜度o(n^3);

2、o(n)列舉每乙個回文子串的中點(偶數長度類似,不做討論)->o(n)向兩端拓展:總複雜度o(n^2)。

如果將上述演算法2視為演算法1的優化版本,那麼manacher演算法可視為演算法2的優化版本。下面先分析演算法2的不足之處。

對於演算法2我們會列舉每乙個位置的中點,每次都從列舉點開始向兩端拓展。比如下圖中列舉了7作為中點,假設其兩端最遠拓展的長度為6,即字串[1~13]為乙個回文子串(並且13為當前向右拓展的最遠距離)。當我們再列舉11作為中點時,我們會依次比較:(10,12)->(9,13)->......,這樣的複雜度就是o(n^2)。然而實際上我們還需要對7為中點的子串進行這樣的列舉嗎?

其實是不需要的。

因為我們已經知道了[1~13]為乙個回文子串了,也就是說子串[1~7]與[7~13]一樣(下圖紅框所示)。那麼子串[9~13]當然與對稱位置的子串[1,5]一樣(下圖藍框所示)。更本質一點就是《以11為中心的回文子串的情況》與《以3為中心的回文子串的情況》一致。而我們是已經求出來了《以3為中心的回文子串的情況》了的,故可以直接求解《以11為中心的回文子串的情況》。

說到這裡貌似還有點問題,因為如果《以3為中心的回文子串的長度》為3呢?我們當然不能直接說[8~14]也是乙個回文子串,因為14以後的情況還未知,也就是說後面的情況需要暴力地去匹配,所以說這還是乙個暴力演算法2333,不過好歹我們已經避免了一段比較了嘛~。由此我們可以歸納得到如下演算法:

首先將上述過程一般化,設:

①mx為當前向右拓展的最遠的下標(上面的13);

②id為該拓展對應的中心點(上面的7);

③對於已經列舉過的中心i,我們用p[i]表示《以i為中心最遠能向兩邊擴充套件的「半徑」長度(包括中心點,即最小為1)>。

④當前列舉的中心位置為x,那麼其關於id的對稱點為2id-x。

由上面的討論可知,《以x為中心的回文子串能最長拓展的「半徑」長度》至少為p[x]=min(p[2id-x],mx-x)(下圖中藍色線條部分)。對於mx以後的情況還需要進行暴力地匹配(下圖中紅色框部分,繼續更新p[x])。如果匹配後發現《以x為中心的回文子串能向右拓展的最遠的下標》大於mx,那麼更新id與mx的值。再接著列舉x以後的點作為回文串中心。

現在還有乙個小問題,那就是上述的過程都是在x=mx呢?首先直接令p[x]=1(因為x>=mx,則前面得到的回文子串的資訊對於《以x為中心的回文串》起不到什麼作用了),然後暴力匹配以x為中心兩邊相同的字元的個數來更新p[x],最後在更新id和mx。

這樣一來按照上述的過程就可以更新所有的點的「拓展半徑」了。至此就基本講完了manacher演算法了,不知道大家是否能看得明白( ̄o ̄) . z z。

上面為什麼說是基本講完了呢?因為在實際應用的過程中,我們又會碰到乙個問題。上述所有的過程都是建立在有乙個id以及乙個回文串中心x的基礎上的,而實際上,當回文串長度為偶數時是沒有中心字元的,這時候這麼處理?

我們將任意兩個字元之間以及字串的首位均插入乙個一樣的字元(比如"#",不會出現在給定的字串即可)得到乙個新的字串,那麼我們可以容易地發現如下事實:

①原來的偶數長度的回文串,可以對應對新字串中乙個以『#』為中心的字串;

②原來的奇數長度的回文串,可以對應對新字串中乙個以原字元為中心的字串;

那麼我們對新的字串使用manacher演算法,求出每乙個下標的p[x],更具p陣列以及兩個字串回文串長度之間的對應關係就可以得到原字串的最長回文串了。對應關係?——p[x]-1即為原字串的回文串長度(舉幾個具體的例子就可以歸納了)。

這樣一來,manacher演算法就算是講完了( ̄▽ ̄)"。

本人也不怎麼會算複雜度,但是介紹乙個演算法,總歸是要解釋以下才行的丫。這裡的複雜度是這麼將為o(n)的呢?考慮字串中每乙個字元的遍歷次數,由於在mx左邊的字元只會再被用來與其右邊進行一次暴力的匹配,在加上每個字元在mx右邊時一定會被匹配一次,那麼乙個字元被用來匹配的次數不會超過兩次,所以總的時間複雜度是o(n)的(逃~)。

//hihocode 1032	最長回文子串

#include#include#include#include#includeusing namespace std;

const int maxn=2e6+10;

char s[maxn],str[maxn];

int n,len1,len2,p[maxn];

void init()

return ans;

}int main(){

scanf("%d",&n);

for(int i=0;i該**是按照自己的理解完成的(實際上理解了原理後寫起來真的沒什麼難度),不保證結果的百分之百正確o,網上當然有十分優秀的板子,這裡掛出來可以看個實現的具體過程。

字串的基礎知識,也是個特別簡單的演算法(實際上碰到地也不多::>_<::>

更多靈活地運用還需要找其它具體的題目進行練習(以後可能續上?)。

字串 最長回文子串

最長回文子串 回文子串 即正著看和倒著看相同的子串,如 abcba yyxyy。由於此類題目為面試筆試常考題目,所以現在就來整理一下啦。1 暴力求解法 最直接的想法就是暴力求解,但是我們可以看到下面的 時間複雜度是o n 3 string findlongeststring string str 暴...

字串 最長回文子串

介紹一下幾個概念 就是從左往右和從右往左讀是一樣的。就如標語 我為人人,人人為我 子串,顧名思義,就是在原字串中的子集,就叫子串。串就是不能分割的,就是連在一起,這個要區別與子串行,子串行就是一段 一段的。列舉各個起點和終點,然後進行判斷該子串是否為回文,最後就是更新最長的回文串。列舉起點和終點 o...

字串 最長回文子串

給定乙個字串 s,找到 s 中最長的回文子串。你可以假設 s 的最大長度為 1000。示例 1 輸入 babad 輸出 bab 注意 aba 也是乙個有效答案。示例 2 輸入 cbbd 輸出 bb 思路 用manacher class solution int max 1 int res 0 儲存最...