Leetcode:【DP】Longest Palindromic Substring 解題報告


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     }
View Code

請戳主頁君的代碼哦

解說:
狀態表達式: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 }
View Code


中心展開法代碼

其它比較牛的解法: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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM