Manacher 演算法 尋找最長回文子串

2022-09-08 03:51:13 字數 2941 閱讀 4563

給定乙個字串,求出其最長回文子串。例如:

s="abcd",最長回文長度為 1;

s="ababa",最長回文長度為 5;

s="abccb",最長回文長度為 4,即 bccb。

以上問題的傳統思路大概是,遍歷每乙個字元,以該字元為中心向兩邊查詢。其時間複雜度為 o(n2),效率很差。

1975 年,乙個叫 manacher 的人發明了乙個演算法,manacher 演算法(中文名:馬拉車演算法),該演算法可以把時間複雜度提公升到 o(n)。

下面來看看馬拉車演算法是如何工作的。

由於回文分為偶回文(比如 bccb)和奇回文(比如 bcacb),而在處理奇偶問題上會比較繁瑣,所以這裡我們使用乙個技巧,具體做法是:在字串首尾,及各字元間各插入乙個字元(前提這個字元未出現在串裡)。

舉個例子:s="abbahopxpo",轉換為s_new="$#a#b#b#a#h#o#p#x#p#o#"(這裡的字元 $ 只是為了防止越界,下面**會有說明),如此,s 裡起初有乙個偶回文abba和乙個奇回文opxpo,被轉換為#a#b#b#a##o#p#x#p#o#,長度都轉換成了奇數

定義乙個輔助陣列int p,其中p[i]表示以 i 為中心的最長回文的半徑,例如:i0

1234

5678

9101112

1314

1516

1718

19s_new[i]$#

a#b#

b#a#

h#o#

p#x#

p#p[i]12

1252

1212

1212

1412

1可以看出,p[i] - 1正好是原字串中最長回文串的長度。

接下來的重點就是求解 p 陣列,如下圖:

設定兩個變數,mx 和 id 。mx 代表以 id 為中心的最長回文的右邊界,也就是mx = id + p[id]

假設我們現在求p[i],也就是以 i 為中心的最長回文半徑,如果i < mx,如上圖,那麼:

if (i < mx)  

p[i] = min(p[2 * id - i], mx - i);

2 * id - i為 i 關於 id 的對稱點,即上圖的 j 點,而p[j]表示以 j 為中心的最長回文半徑,因此我們可以利用p[j]來加快查詢。

#include #include #include using namespace std;

char s[1000];

char s_new[2000];

int p[2000];

int init()

s_new[j] = '\0'; // 別忘了哦

return j; // 返回 s_new 的長度

}int manacher()

max_len = max(max_len, p[i] - 1);

}return max_len;

}int main()

return 0;

}

文章開頭已經提及,manacher 演算法為線性演算法,即使最差情況下其時間複雜度亦為 o(n),在進行證明之前,我們還需要更加深入地理解上述演算法過程。

根據回文的性質,p[i]的值基於以下三種情況得出:

(1):j 的回文串有一部分在 id 的之外,如下圖:

上圖中,黑線為 id 的回文,i 與 j 關於 id 對稱,紅線為 j 的回文。那麼根據**此時p[i] = mx - i,即紫線。那麼p[i]還可以更大麼?答案是不可能!見下圖:

假設右側新增的紫色部分是p[i]可以增加的部分,那麼根據回文的性質,a 等於 d ,也就是說 id 的回文不僅僅是黑線,而是黑線+兩條紫線,矛盾,所以假設不成立,故p[i] = mx - i,不可以再增加一分。

(2):j 回文串全部在 id 的內部,如下圖:

根據**,此時p[i] = p[j],那麼p[i]還可以更大麼?答案亦是不可能!見下圖:

假設右側新增的紅色部分是p[i]可以增加的部分,那麼根據回文的性質,a 等於 b ,也就是說 j 的回文應該再加上 a 和 b ,矛盾,所以假設不成立,故p[i] = p[j],也不可以再增加一分。

(3):j 回文串左端正好與 id 的回文串左端重合,見下圖:

根據**,此時p[i] = p[j]p[i] = mx - i,並且p[i]還可以繼續增加,所以需要

while (s_new[i - p[i]] == s_new[i + p[i]]) 

p[i]++;

根據(1)(2)(3),很容易推出 manacher 演算法的最壞情況,即為字串內全是相同字元的時候。在這裡我們重點研究 manacher() 中的 for 語句,推算發現 for 語句內平均訪問每個字元 5 次,即時間複雜度為:tworst(n)=o(n)。

同理,我們也很容易知道最佳情況下的時間複雜度,即字串內字元各不相同的時候。推算得平均訪問每個字元 4 次,即時間複雜度為:tbest(n)=o(n)。

綜上,manacher 演算法的時間複雜度為o(n)

Manacher演算法 hdu3068 最長回文

留個回文串的版qaq 這個演算法。和擴充套件kmp是一樣的。不過乙個是順著乙個是回文。int far 0,ans 0 for int i 1 s i i 題目點這裡 模板題。然後它倒騰了我好久 一直wa wa wa。orz 為了防止要特判回文串長度的奇偶性。把每個字元中間塞乙個莫名其妙的的字元就行t...

HDU3068 manacher演算法 最長回文串

求最長的回文串。有一次用dp求過一次。我們都知道求回文串可以依賴於暴力的方法 以某點為重心,暴力的比唄 manacher方法的思想在於利用對稱性來減少暴力運算,從而提高效率。從左到右遍歷字元,記錄最大的 回文串的右界 記當時的 字元位置為i 分兩種情況 1 當前遍歷字元x在 右界右邊。這時候無法利用...

尋找最長回文子串Manacher演算法學習筆記

首先是用特殊符號比如 插到原字串每個字元之間的辦法,使得字串有了奇數個字元,這樣就永遠存在乙個中間字元,很巧妙。這不是關鍵。這個演算法的關鍵是利用了已匹配回文串當前中點center左邊的字元i i 的下標為2 center i 兩邊的對稱匹配情況 即p i 的值 從而得出中點center右邊對稱點i...