求最長回文子串的演算法比較經典的是manacher演算法,下面寫寫自己的理解。
(文中用到的來自這裡,博主寫的很好,由於為了和**一致,我稍微p了一下。)
首先,說明一下用到的陣列和其他引數的含義:
(1)以字串中下標為
例如:字串
所以,陣列
(2)為當前已確定的邊界能伸展到最靠右的回文串的中心。
例如:
設黑色的線段表示字串範圍,紅色的線段表示當前已經確定的回文子串在主串中的範圍,雖然左邊的紅色線段(左邊的回文子串)長度比右邊長,但是,記錄的中心是右邊紅色線段的中心,因為右邊的回文子串伸展更靠右。(下面將會解釋為什麼要記錄這個id值)
manacher演算法是從左到右掃瞄的,所以,在計算
假設現在掃瞄到主串下標為為中心的回文子串。至於這個交集有什麼用,下面將解釋。
以i為中心的回文串和以id為中心的回文串如果有交集,會出現三種情況:
其中,2*id-i為i以id為中心的對稱位置。(2*id-i這個就不用多說了吧,計算一下就得到了)
第一種情況是(上圖),以2*id-i為中心的回文子串左端超出了以id為中心的回文子串(綠色部分左端超出黑色部分左端)。那麼,根據回文串的特點,知道以2*id-i為中心的兩邊橙色部分是對稱的,同樣,若以id為中心,這兩段橙色部分又對稱id右邊兩段橙色部分,所以,以i為中心的兩段橙色部分也是回文串。
這種情況下,
那麼,以i為中心的橙色部分有沒有可能更長?這是不可能的,假設還可以更長,如下:
a和b對稱,b和c對稱,c和d對稱,最終得到a和d對稱,那麼,以id為中心的回文串長度就不是下面黑色部分的長度了,而應左端加a右端加d,與已經求得的長度矛盾。
所以這種情況下
第二種情況是以2*id-i為中心的回文子串在以id為中心的回文子串內,如上圖。此時,
同樣,c和d對稱,b和c對稱,a和d對稱,得到a和b對稱,那麼以2*id-i為中心的回文子串長度就不是綠色部分的長度了,需要左端加a右端加b,與以求得的長度矛盾。
所以,這種情況下
第三種情況是,以2*id-i為中心的回文子串左端與以id為中心的回文子串的左端恰好重合。則有
所以,需要用乙個while迴圈來確定它能增加多少。while迴圈為:
while (s[i - p[i]] == s[i + p[i]])
++p[i];
也就是判斷綠色兩端(下圖淺黑色線段)是否相同,如果相同,就可以不斷增加。(理解p[i]的意思,就理解這個迴圈了)
如果沒有出現交集(上面三種情況),那麼就以i為中心點找最長回文子串(下面**中else的情況)。所以,演算法主要是利用回文串的交集來減少計算。
如果我們將上面的情況總結起來,**將非常簡潔:
if (p[id] + id - 1 >= i)//沒有超出當前邊界最右的回文串,也就是上面出現交集三種情況中的一種
p[i] = min(p[2 * id - i], p[id] + id - i);
else//如果沒有交集,就以它為中心求回文串長度
p[i] = 1;
while (s[i - p[i]] == s[i + p[i]])
++p[i];
最後,需要注意的是,上面的討論都是以某個字元為中心的回文串,比如像這樣的回文串:aabaa(長度為奇數)。但是,如果是這樣的回文串:aabbaa(長度為偶數),就沒法處理。
我們可以通過插入特殊符號(如『#』)的辦法,將字串統一為奇數長度,如aabaa變為#a#a#b#a#a# ;同理,aabbaa變為#a#a#b#b#a#a#
注意到,上面的**:
while (s[i - p[i]] == s[i + p[i]])
++p[i];
可能越界(超過頭或尾),我們可以通過頭尾也加入不相同的特殊符號處理,如aabaa變為$#a#a#b#a#a#@。這種辦法為什麼可行呢?我們舉個例子,還是以aabaa為例,它變成$#a#a#b#a#a#@。當i指向第乙個a時(也就是i=2),這時,s[i-1]==s[i+1];繼續比較s[i-2]≠s[i+2](也就是比較$和a),就不會超過頭。所以,就避免了越界的現象。末尾加個@也是同樣的道理。
下面是hihocoder的一道求最長回文子串的題:
我的ac**:
#include#includeint min(int a, int b)
int lps(std::string &s)
int *p = new int[new_s_len + 1];
int id = 0;//記錄已經查詢過的邊界最靠右回文串的中心
int maxlps = 1;
p[0] = 1;
for (int i = 1; i < new_s_len-1; ++i)
delete p;
return maxlps - 1;
}int main()
{ int n;
std::string s;
std::cin >> n;
while (n--)
{ std::cin >> s;
std::cout<
在插入特殊符號($ # @)時,剛開始用insert()函式,超時了,應該是不斷insert的原因。後來乾脆直接用乙個新的字串儲存處理後的字串,就通過了。
hihoCoder 1032 最長回文子串
求最長回文串的長度,一道模板題,如果大家有誰不懂這個演算法,可以到這個部落格上看一下,我感覺寫的非常好,仔細看一定能看懂。manacher演算法 include include include include using namespace std const int maxn 1000005 ch...
HihoCoder 1032 最長回文子串
華電北風吹 天津大學認知計算與應用重點實驗室 2016 06 23 題目分析 首先需要注意的是 子串 和 子串行 問題還有很多變形,如最長公共子串 子串行 最長回文子串 回文子串行 等。problem1032.cpp 定義控制台應用程式的入口點。最長回文子串 張正義 2016 04 12 inclu...
hihoCoder 1032 最長回文子串
這就是最典型的manacher演算法題,聽起來高大上,而且也很難說清楚,但是實際上並不難,就相當於數學的推理,推出乙個公式即可直接解出。題目如下 時間限制 1000ms 單點時限 1000ms 記憶體限制 64mb 描述 小hi和小ho是一對好朋友,出生在資訊化社會的他們對程式設計產生了莫大的興趣,...