題目:
Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.
翻譯:
給定一個字符串,找出它的最大回文子串,返回。
分析:
所謂”迴文“,是指倒着讀和正着讀結果都一樣的字符串,例如”abcba"或“abba"。
這個題目有很多種解法,下面將依次進行分析。
1.Brute force (暴力破解法)
即依次得到給定字符串的每一個子串,判斷這個子串是否爲迴文串,找到最長迴文子串。
暴力破解法獲取每個子串用了兩個for循環(O(n^2)),在判斷該子串是否爲迴文時又用了一個while循環(O(n)),所以時間複雜度爲O(n^3)。
代碼如下,這段代碼在OJ上是沒法通過的,因爲當字符串很長時會超時。
class Solution {
public:
string longestPalindrome(string s) {
int startLP=0;//記錄最長迴文子串的起始位置
int maxLen=0;//記錄最長迴文子串的長度
for(int i=0;i<s.length();i++)
for(int j=i+1;j<s.length();j++)
{
int start=i,end=j;
while(start<=end&&s[start]==s[end])//判斷該子串是否爲迴文
{
start++;
end--;
}
if(start>end)//若該子串爲迴文子串
{
if((j-i+1)>maxLen)
{
startLP=i;
maxLen=j-i+1;
}
}
}
return s.substr(startLP,maxLen);
}
};
2.Dynamic Programming(動態規劃)
動態規劃是暴力法的一個進化版本,我們沒必要每一次都重新判斷一個子串是否爲迴文串,可以將一些輔助判斷的信息記錄下來供後來者使用,這樣後面的迴文串判斷就僅需O(1)的時間,而不是暴力法中的O(n)。若字符串從i..j的子串是迴文,則字符串總(i+1)...(j-1)的子串也是迴文,由此最長迴文子串就可以分解成一序列子問題。
定義P[i, j]=1表示子串s[i,..., j]是迴文串,P[i, j]=0表示子串s[i,..., j]是不是迴文串,則得到狀態方程和轉移方程爲:
因此,使用動態規劃的方法來求解需要額外O(n^2)空間,時間複雜度也爲O(n^2)。
在具體實現中,由於判斷長度爲n的子串是否爲迴文串需要藉助長度爲(n-2)的子串是否爲迴文串來判斷,因此自底向上進行計算,初始化長度爲1和長度爲2的子串的P[i, j],然後依次計算長度爲3,4,... ,s.length()的子串的P[i, j],計算過程同時更新最長迴文子串的起始位置和長度。代碼如下:
class Solution {
public:
string longestPalindrome(string s) {
int P[s.length()][s.length()]={0};
int startLP=0;//記錄最長迴文子串起始位置
int maxLen=0;//記錄最長迴文子串長度
for(int i=0;i<s.length();i++)
{
P[i][i]=1;
startLP=i;
maxLen=1;
}
for(int i=0;i<s.length()-1;i++)
{
if(s[i]==s[i+1])
{
P[i][i+1]=1;
startLP=i;
maxLen=2;
}
}
for(int len=3;len<=s.length();len++)
{
for(int i=0;i<s.length()-len+1;i++)
{
int j=i+len-1;
if(s[i]==s[j]&&P[i+1][j-1]==1)
{
P[i][j]=1;
startLP=i;
maxLen=len;
}
}
}
return s.substr(startLP,maxLen);
}
};
(這裏有一個小疑惑,一樣的思路,我用C#寫的時候,當字符串過長時會發生Memory Limit Exceeded,但是重新用C++寫了之後,就不出現這樣的問題了,這是爲什麼?好吧,原諒我這放蕩不羈的編程語言,一會兒用這,一會兒用那~)
3. 中心擴展法
中心擴展法的基本思想是依次以每一個字符爲中心,向兩邊擴展,查找以這個字符未中心的最長迴文串,從而找到最終的最長迴文串。需要注意的是迴文串長度爲單數和雙的情況,例如:“abcba"和”abba",因此在向兩邊擴展時需要考慮以該字符爲中心的擴展,以及以該字符及其下一個字符爲中心的擴展,這樣才能包含迴文串長度爲雙數的情況。
中心擴展法需要遍歷一遍字符串對每一個字符進行擴展,擴展的時間複雜度爲O(n),由此得到中心擴展法的時間複雜度爲O(n^2),空間複雜度爲O(1)。代碼如下:
class Solution {
public:
string expansion(string s,int start1,int start2)
{
int l=start1,r=start2;
while(l>=0&&r<s.length()&&s[l]==s[r])
{
l--;
r++;
}
return s.substr(l+1,r-1-l);
}
string longestPalindrome(string s) {
if(s.length()==1)
return s;
string lp="";
for(int i=0;i<s.length()-1;i++)
{
string tempStr=expansion(s,i,i);
if(tempStr.length()>lp.length())
lp=tempStr;
tempStr=expansion(s,i,i+1);
if(tempStr.length()>lp.length())
lp=tempStr;
}
return lp;
}
};
4.Manacher算法
......明天繼續