題目描述:給定乙個字串,求它的最長回文子串(子串是連續的)的長度
解法一:列舉法
外面的雙層for迴圈確定連續子串頭尾,加上裡面的乙個for迴圈來判斷該子串是否為回文串,此外用乙個max變數(初始值為1)動態更新儲存最長子回文串的長度,時間複雜度為o(n^3),空間複雜度為o(1)。
#include #include using namespace std;
bool ispalindrome(string &str, int start, int end)
return true;
}int longestpalindrome(string &str)
} return maxlen;
}int main()
return 0;
}
解法二:中心擴充套件法乙個for迴圈將字串中的每個元素作為中心點,並對該中心點是奇數回文串中心還是偶數回文串中心分開寫程式,在該中心點位置上進行擴充套件,依次比較兩邊的對應元素是否相等,時間複雜度為o(n)*o(n/2+n/2)=o(n^2),程式**如下:
#include #include using namespace std;
int longestpalindrome(string &str)
if(maxlen < 2*(j-1) + 1) //注意跳出的j的取值,如asdsf,j=2時跳出,而最長回文子串為:2*(j-1)
maxlen = 2*(j-1) + 1;
//該點為偶數數字符數的回文串中心
for(j = 0; i-j>=0 && i+j+1 <= str.size()-1; ++j)
if(maxlen < 2*(j-1) + 2)
maxlen = 2*(j-1) + 2;
} return maxlen;
}int main()
return 0;
}
解法三:manacher演算法步驟如下:
a,像下面這樣對字串12212321進行插字元
s[i]:*#1#2#2#1#2#3#2#1#
p[i]: 12125214121612121
即先在原字串中每個字元左右插字元#,最後在最前面插字元*。p[i]是以該字元為中心的最長回文子串的右半邊長度(含中心本身)。插入*可以更好的處理越界問題,字元間插入#是為了使所有回文子串都變為奇數,這樣無需像中心擴充套件法那樣還要考慮偶數的問題。
b,接下來是如何求p[i]
這就像動態規劃一樣,先求p[0],再求p[1],再由p[0]和p[1]求p[2],...,再由p[0]~p[i-1]求p[i],...。我們以前用到動態規劃時,如求兩字串的最長公共子串,根據dp[i-1][j-1],dp[i][j-1],dp[i-1][j]來求dp[i][j],故我們需要確定p[i]的**。而在manacher演算法中p[i]的**有兩種,一是若p[i]關於id(下一句解釋id含義)與p[j]對稱,那麼p[i]可由p[j]求出,二是無法使用前面的p[j]時,直接依次比較s[i]兩邊的元素是否相等。
由於之前已經知道以s[1]~s[i-1]為中心的最大回文串長,此時定義兩個變數mx和id,mx指s[1]~s[i-1]為中心的所有最長回文子串中能延伸到的最右端的位置,且是以s[id]為中心的,故mx = id + p[id]。故mx為以id為中心的回文子串右半段的下乙個位置(不在回文串內)。
由此時,分兩種大情況:
1,若mx<=i,此時無法使用s[id]為中心來求得s[i],只能以它為中心依次判斷兩邊元素是否相等。**將p[i]當成i,這樣寫,p[i]=1;while(s[i+p[i] == s[i-p[i]) ++p[i];
2,若mx>i,以以下三種情況進行分析:
a, 如下圖,s[2*id-i]的回文子串完全在s[id]的回文子串範圍時,則根據對稱性,p[i] = p[2*id-i]。
p[i]不可能比這大或比這小,假設p[i]比p[2*id-i]大,如多出如下的1和2部分,由於該範圍是關於id對稱的,最後也會得出p[2*id-i]應該變大,多出3和4部分,矛盾;若假設比它小也會產生相似的矛盾。故p[i] = p[2*id-i]。
b,如下圖,s[2*id-i]的回文子串的左半邊等於id的左側範圍的最左側位置,而下面黑色部分1與紅色部分2不同,兩個紅色部分2,3對稱相等,而藍色部分4有可能與紅色部分3相同,故先令p[i]=mx-i; 再while(s[i+p[i] == s[i-p[i]) ++p[i];
c,如下圖,s[2*id-i]的回文子串的左半邊超過id的左側範圍,故此時p[i]=mx-i。不可能更長了,假設更長,及下面的部分1和部分2相等,而部分2和部分3關於id對稱相等,而部分3和部分4關於2*id-i對稱相等,故部分1和部分4相等,進而推出id的範圍超過了綠色範圍,矛盾。
對於2中的情況a和情況c,令p[i] = p[2*id-i]後, 再執行while(s[i+p[i] == s[i-p[i]) ++p[i];也不會對結果有什麼影響,因為肯定不滿足條件而無法進入while中處理,s[i]依然等於s[2*id-i]。
故核心**為:
if(mx > i) p[i] = min(p[2*id-i], mx-i);
else p[i] = 1;
while(s[i+p[i] == s[i-p[i]) ++p[i];
c,接下來是重新放置id的位置,只需比較i和舊的id誰能向右邊延伸最遠。
完整演算法**如下:
#include #include #define max 110
using namespace std;
int p[2*max + 2];
int manacher(char *s)
s[0] = '$';
s[2*len + 1] = '*';
s[2*len + 2] = '\0';//最後需要加'\0',避免越界
int id = 0, maxlen = 0;
p[0] = 1;
for(i = 1; i <= 2*len+1; ++i)
return maxlen-1;
}int main()
return 0;
}
總結:manacher演算法的時間複雜度一般為o(n),但我認為它的時間複雜度應該比o(n)要大。該演算法和中心擴充套件法差不多,依次從下標1開始從小到大來分別計算以該點為中心的最長回文子串,每次遍歷時,如果可以利用到s[id*2-i]就能省去p[id*2-i]次比較,如果不能利用則還是需要以該中心點依次比較它兩邊的元素。
舉一反三:
輸出最長回文子串:將乙個很長的字串分割成一段一段的子串,要求子串都是回文串,且每次輸出最長的回文串。如輸入"habbafgh",輸出"h","abba","f","g","h"。
可以使用中心擴充套件法將所有回文子串輸出,可能考慮到輸出不能重複的問題,可設定vecter來對每次輸出時進行比較。
也可用曼徹斯特演算法根據得到的p將所有的子回文串輸出,設定vector來解決輸出重複問題。
程式設計題 最長回文子串
對於乙個字串,請設計乙個高效演算法,計算其中最長回文子串的長度。給定字串a以及它的長度n,請返回最長回文子串的長度。測試樣例 abc1234321ab 12返回 7 解題思路 法一 中心擴散法 時間複雜度o n 2 空間複雜度o n 遍歷每個字元,以該字元為中心,向前 後擴散,直到不滿足回文時停下。...
最長回文子串 最長回文子串行
1.最長回文子串行 可以不連續 include include include include using namespace std 遞迴方法,求解最長回文子串行 intlps char str,int i,int j intmain include include include using n...
最長回文子串
描述 輸入乙個字串,求出其中最長的回文子串。子串的含義是 在原串連續出現的字串片段。回文的含義是 正著看和倒著看是相同的,如abba和abbebba。在判斷是要求忽略所有的標點和空格,且忽略大小寫,但輸出時按原樣輸出 首尾不要輸出多餘的字串 輸入字串長度大於等於1小於等於5000,且單獨佔一行 如果...