給定乙個字串,求出其最長回文子串。例如:
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;文章開頭已經提及,manacher 演算法為線性演算法,即使最差情況下其時間複雜度亦為 o(n),在進行證明之前,我們還需要更加深入地理解上述演算法過程。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;
}
根據回文的性質,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...