最長子迴文串
5. Longest Palindromic Substring
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
Example 1:
Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.
Example 2:
Input: "cbbd"
Output: "bb"
動態規劃
如果一個字符串爲迴文串,那麼除去他的首尾一個字符,得到的新字符依舊是迴文串。即對於一個字符串S有
p(i,j) = p(i+1,j-1) and Si = Sj。P(i,j)表示從i到j的字符串。
從表達式可以看出,需要知道p(i,j),必須先知道p(i+1,j-1)。不如將外層循環倒置過來,先求i+1的值,再求j-1的值。
還有一點要注意的是初始值,關於j - i < 3。如果長度爲1的話,那麼這個字符串必然爲迴文串,如果長度爲2的話,又滿足Si = Sj 那麼他也必然是迴文串。即他們不受上述表達式的約束。
public String longestPalindrome(String s) {
if(s == null || s.length() < 1){
return "";
}
int len = s.length();
boolean[][] dp = new boolean[len][len];
int subLength = 0;
String res = null;
for(int i = len - 1; i>= 0 ;i--){
for(int j = i; j < len; j++){
dp[i][j] = s.charAt(i) == s.charAt(j) &&(j - i < 3 || dp[i+1][j-1]);
if(dp[i][j] && (res == null || j - i + 1 > res.length())){
res = s.substring(i,j+1);
}
}
}
return res;
}
中心擴展算法
首先先看一下回文串有什麼特點,迴文串的最大特點就是中心對齊。如“ababa",他是以a爲中心對齊。那麼對於“abba”這種偶數的迴文串,同樣也是中心對齊的。他以兩個b之間的字符之間爲中心。因此我們可以以數組的每一個元素作爲中心,找出最大的迴文串。
我們可以設置迴文串的頭尾指針,start 和 end。如果有比目前的迴文串更長的迴文串,則修改start和end的的位置。這樣子時間複雜度爲O(n^2),空間複雜度爲O(n)。
public String longestPalindrome(String s) {
if(s == null || s.length() < 1){
return "";
}
int sLen = s.length();
int start = 0,end = 0;
for(int i = 0; i < sLen; i++){
int len1 = expandAroundCenter(s,i,i);
int len2 = expandAroundCenter(s,i,i+1);
int len = Math.max(len1,len2);
if(len > end - start){
start = i - (len - 1)/2;
end = i + len/2;
}
}
return s.substring(start,end+1);
}
private int expandAroundCenter(String s,int left,int right){
int L = left,R = right;
while(L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)){
L--;
R++;
}
return R - L -1;
}
馬拉車算法(Manacher’s Algorithm)
馬拉車算法充分應用了迴文串的對稱性。和中心擴展法一樣,我們以某個節點爲中心,分別向兩邊擴展。爲了保持字符的個數爲奇數,在字符與字符之間插入特殊符號“#”來保證出力的字符個數是爲奇數的。如“abab”變爲“#a#b#a#b#”。假設新的字符串爲T,我們使用P[i] 表示 T[i]處的迴文半徑。如以T[i]爲中心的最長迴文串爲T[l,r],則P[i] = r - i + 1。這樣求最長迴文串的問題就轉換爲求數組P的問題了。
數組P怎麼求,考慮。center 表示之前取得最大回文串的中心位置。right表示最大回文串能到達的最右端的值。
- 若i<right,則i之前的值必然計算出來了。我們可以利用迴文串的性質。找出點i關於center的對稱點j。j = 2* center - i。
- 如果P[j] < right -i。即以j爲中心的迴文串沒有超出[left,right]的範圍,由迴文串的性質知道,兩端的對應字符應該是相等的。所以有P[j] = P[i]。
- 如果P[j] >= right -i。即表明以j爲中心的最大回文串超出了範圍。在範圍內的部分肯定是複合迴文串的性質,但是對於超出的部分,只要從right+1開始一一匹配,從而更新right和對應的center,
- 若i > right 無法利用迴文串的性質,只能一步一步去匹配。
public String longestPalindrome(String s) {
if(s == null || s.length() < 1){
return s;
}
int sLen = s.length();
StringBuilder t = new StringBuilder();
// t.append("$");
t.append("#");
for(int i = 0; i < sLen; i++){
t.append(s.charAt(i));
t.append("#");
}
int tLen = t.length();
int center = 0;
int right = 0;
int maxCenter =0;
int maxLen = 1;
int[] p = new int[2 *sLen + 1];
for(int i = 0; i < tLen; ++i){
p[i] = i<right?Math.min(p[2*center -i],right -i):1;
while(i - p[i] >= 0 && i + p[i]<tLen && t.charAt(i - p[i]) == t.charAt(i + p[i])){
p[i]++;
}
if(right < i + p[i]){
right = i + p[i];
center = i;
}
if(maxLen < p[i]){
maxLen = p[i];
maxCenter = i;
}
}
return s.substring((maxCenter - maxLen + 1)/2,(maxCenter - maxLen + 1)/2 + maxLen - 1);
}