求最長回文子串 Manacher 演算法

2021-08-06 08:23:03 字數 2953 閱讀 2629

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

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

(2):s="ababa", 最長回文長度為 5;

(3):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];

intinit

() 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演算法 O n 求最長回文子串

樸素的做法是求出以每個字元為中心的回文串長度,複雜度為 而manacher演算法可以在o n 時間內求解,奇數長度和偶數長度可以統一處理。根據回文串的對稱性,避免了大量不必要的比較。處理技巧 相鄰的字元之間插入乙個分隔符,串的首尾也要加,以 為例,則長度為n的字串經過處理之後變成2n 1奇數長度的字...

求最長回文子串 ManaCher演算法

最長回文子串 求任一既定字串中,回文子串的最長長度 這是最容易想到的辦法。先遍歷獲得字串的所有子串,再對每個子串判斷其是不是回文串。對於長度為n的字串,子串個數為n n 1 2,加上對每個子串進行判斷,這種解法的時間複雜度為o n 3 就不寫了,下面重點介紹manacher演算法 由回文的定義可以推...

Manacher演算法 求最長回文子串

首先,得先了解什麼是回文串。回文串就是正反讀起來就是一樣的,如 abcdcba o n 3 o n 2 我們要是直接採用暴力方法來查詢最長回文子串,時間複雜度為o n 3 好一點的方法是列舉每乙個字元,比較較它左右距離相鄰的點是否相等,我們姑且稱之為中心檢測法,時間複雜度為o n 2 當我們遇到字串...