LeetCode演算法題5:最長迴文子串解析
阿新 • • 發佈:2018-11-21
給定一個字串 s,找到 s 中最長的迴文子串。你可以假設 s 的最大長度為 1000。
示例 1:
輸入: "babad"
輸出: "bab"
注意: "aba" 也是一個有效答案。
示例 2:
輸入: "cbbd"
輸出: "bb"
這個題可以暴力法搜尋,設定一個數組,儲存每個位置字元為中心的迴文字串長度,最後輸出最長的,但是這樣的時間複雜度是O(n^2),有一種專門用來搜尋最長迴文子串的快速演算法叫Manacher演算法,其複雜度是線性的。
演算法的思路如下:(來自百度百科)
Manacher演算法提供了一種巧妙的辦法,將長度為奇數的迴文串和長度為偶數的迴文串一起考慮,具體做法是,在原字串的每個相鄰兩個字元中間插入一個分隔符,同時在首尾也要新增一個分隔符,分隔符的要求是不在原串中出現,一般情況下可以用#號。
輔助陣列:
Manacher演算法用一個輔助陣列Len[i]表示以字元T[i]為中心的最長迴文字串的最右字元到T[i]的長度,比如以T[i]為中心的最長迴文字串是T[l,r],那麼Len[i]=r-i+1。如下圖:
![輔助陣列](https://img-blog.csdnimg.cn/20181120094957624.jpg)
Len 陣列有一個性質,那就是Len[i]-1就是該回文子串在原字串S中的長度。
Len陣列的計算:
首先從左往右依次計算Len[i],當計算Len[i]時,Len[j] (0<=j<i)已經計算完畢。設P為之前計算中最長迴文子串的右端點的最大值,並且設取得這個最大值的位置為po,分兩種情況:
第一種情況:i<P
那麼找到i相對於po的對稱位置,設為j,那麼如果Len[j]<P-i,如下圖:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20181120095257862.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3g2MDM1NjA2MTc=,size_16,color_FFFFFF,t_70)
那麼說明以j為中心的迴文串一定在以po為中心的迴文串的內部,且j和i關於位置po對稱,由迴文串的定義可知,一個迴文串反過來還是一個迴文串,所以以i 為中心的迴文串的長度至少和以j為中心的迴文串一樣,即Len[i]>=Len[j]。因為Len[j]<P-i,所以說i+Len[j]<P。由對稱性可知Len[i]=Len[j]。
如果Len[j]>=P-i,由對稱性,說明以i為中心的迴文串可能會延伸到P之外,而大於P的部分我們還沒有進行匹配,所以要從P+1位置開始一個一個進行匹配,直到發生失配,從而更新P和對應的po以及Len[i]。
所以其實Len[i]取的是Len[j]與P-i的最小值。
第二種情況:i>=P
如果i比P還要大,說明對於中點為i的迴文串還一點都沒有匹配,這個時候,就只能老老實實地一個一個匹配了,匹配完成後要更新P的位置和對應的po以及Len[i]。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20181120100536249.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3g2MDM1NjA2MTc=,size_16,color_FFFFFF,t_70)
所以思路也就是這樣了,程式也就出來了。
C++原始碼:
class Solution {
public:
string longestPalindrome(string s) {
string T = "*#";
for(int i=0;i<s.length();i++)
{
T += s[i];
T += "#";
}
int maxLen = 0, maxn = 0;
int P = 0;
int po = 0;
int p[T.length()] = {0};
for(int i=0;i<T.length();i++)
{
if(P>i)
p[i] = min(p[2*po-i], P-i);
else
p[i] = 1;
while(T[i-p[i]]==T[i+p[i]])
p[i]++;
if(p[i]+i>P)
{
P = p[i]+i;
po = i;
}
if(maxLen<p[i])
{
maxLen = p[i];
maxn = i;
}
}
return s.substr((maxn-maxLen)/2, maxLen-1);
}
};
python3原始碼:
class Solution:
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
T = "*#"
for i in range(len(s)):
T += s[i]
T += "#"
T += "$"
po = 0
maxLen = 0
maxn = 0
p = [0]*len(T)
for i in range(2, len(T)-1):
if p[po]+po>i:
p[i] = min(p[po*2-i], p[po]+po-i)
else:
p[i] = 1
while T[i-p[i]]==T[i+p[i]]:
p[i] += 1
if p[i]+i>p[po]+po:
po = i
if p[po]>maxLen:
maxLen = p[po]
maxn = po
return s[(maxn-maxLen)//2:(maxn-maxLen)//2+maxLen-1]