Longest Palindromic Substring -- HARD 級別
Question SolutionGiven 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.
經典的DP題目。
主頁君給出3種解法:
1. Brute Force
GitHub代碼鏈接
這個非常Straight Forward,就是開始節點i從0-len-1遍歷一次,結束結點以i - Len-1遍歷一次。每個字符串都
判斷它是不是回文,判斷是不是回文也可以使用遞歸來做。總的復雜度是
Time:O(n^3), Space:O(1)
2. DP
DP 因為是二維動態規划
Time:O(n^2), Space:O(n^2)

1 public String longestPalindrome(String s) { 2 if (s == null) { 3 return null; 4 } 5 6 String ret = null; 7 8 int len = s.length(); 9 int max = 0; 10 11 boolean[][] D = new boolean[len][len]; 12 13 for (int j = 0; j < len; j++) { 14 for (int i = 0; i <= j; i++) { 15 D[i][j] = s.charAt(i) == s.charAt(j) && (j - i <= 2 || D[i + 1][j - 1]); 16 if (D[i][j]) { 17 if (j - i + 1 > max) { 18 max = j - i + 1; 19 ret = s.substring(i, j + 1); 20 } 21 } 22 } 23 } 24 25 return ret; 26 }
請戳主頁君的代碼哦
解說:
狀態表達式:D[i][j] 表示i,j這2個索引之間的字符串是不是回文。
遞推公式: D[i][j] = if ( char i == char j) && (D[i + 1][j - 1] || j - i <= 2)) 這個很好理解,跟遞歸是一個意思,只不過 動規的好處就是我們可以重復利用這些結果。
初始化:
D[i][i] = true;實際上也不用特別初始化,都可以套到遞推公式里頭。 所以主頁君的代碼會看起來很簡單。
注意:用max記錄回文長度,回文找到時,更新一下max,及結果的起始地址,結束地址。
現在重點來了,我們怎么設計這個動規才可以重復利用呢?
從這里可以看出D[i + 1][j - 1], 我們推i,j的時候用到了i+1, j-1,其實意思就是在計算i,j時,關於同一個j-1的所有的i必須要計算過。
畫圖如下:
1. 00
2. 00 01
11
3. 00 01 02
11 12
22
3. 00 01 02 03
11 12 13
22 23
33
看到上面的遞推關系了嗎?只要我們一列一列計算,就能夠成功地利用這個動規公式。這也是動態規划的關鍵性設計所在。
如果你不知道如何設計,就和主頁群一樣,畫一個圖來看我們計算某值的時候,需要哪些已經有的值。如上圖所示,我們需要的是i+1, j - 1,實際上就是左下角的值,這樣的話我們只要一列一列計算,就能成功動態規划。
注意:一行一行計算就會失敗!
所以我們的循環的設計是這樣的:
for (int j = 0; j < len; j++)
{ for (int i = 0; i <= j; i++) {
具體請參見代碼,慢慢感受一下。這個才是動規的精髓咯。
3. 中心展開法。
這個方法其實很直觀,就是從頭掃描到尾部,每一個字符以它為中心向2邊擴展,擴展到不能擴展為止(有不同的字符),返回以每一個字符為中心的回文,然后不斷更新最大回文並返回之。
算法簡單,而且復雜度為O(n^2),空間復雜度為O(1)
推薦面試使用這一種方法。據說有的公司如EBAY會拒掉動規的解法. ORZ.. 其實主頁君比較心水第二種解法啊,多優美,第三種解法雖然消耗更少,但沒有什么普適性。

1 public class Solution { 2 public String longestPalindrome(String s) { 3 if (s == null) { 4 return null; 5 } 6 7 String ret = null; 8 9 int len = s.length(); 10 int max = 0; 11 for (int i = 0; i < len; i++) { 12 String s1 = getLongest(s, i, i); 13 String s2 = getLongest(s, i, i + 1); 14 15 if (s1.length() > max) { 16 max = Math.max(max, s1.length()); 17 ret = s1; 18 } 19 20 if (s2.length() > max) { 21 max = Math.max(max, s2.length()); 22 ret = s2; 23 } 24 } 25 26 return ret; 27 } 28 29 public String getLongest(String s, int left, int right) { 30 int len = s.length(); 31 while (left >= 0 && right < len) { 32 // when i is in the center. 33 if (s.charAt(left) != s.charAt(right)) { 34 break; 35 } 36 37 left--; 38 right++; 39 } 40 41 return s.substring(left + 1, right); 42 } 43 }
中心展開法代碼
其它比較牛的解法:http://blog.csdn.net/hopeztm/article/details/7932245
Ref: http://blog.csdn.net/fightforyourdream/article/details/21309759
http://blog.163.com/zhaohai_1988/blog/static/2095100852012716105847112/
http://blog.csdn.net/fightforyourdream/article/details/15025663