1. 程式人生 > >#5 Longest Palindromic Substring

#5 Longest Palindromic Substring

這道題主要是有三個變數。因為要確定一個substring,就有start point和end point,這組成了兩個變數。然後其次,我們至少對每個substring從頭到尾loop一遍,來確定這個substring是不是palindrome,這又需要一個n。所以,如果用暴力解法的話,這道題的time complexity是O(n^3)。如果我們要用這種方法的話,我們不用記錄這個string,只需要用start point和end point兩個index來記錄string即可。所以暴力解法的space complexity是O(1)。

 

我的問題:用dynamic programming寫出來了,但是依靠了hint和solution。

1. 先check input string是不是empty string,如果是empty的話,直接return空的string作為結果。

2. 建立了一個2D array來記錄palindrome的長度。count[i][j] means from index i to index j (included). 如果這個區間範圍只有一個數字的話,把count array裡的內容initialize成1,因為一個數字它本身就是palindrome。如果區間範圍大於一個數字的話,就把count array裡的內容initialize成0,先把default設成沒有palindrome。

3. 然後用一個nested loop來確定palindrome。According to dynamic programming, we need to find both base case and indective steps. 

Here, we have two base cases, the first is for one character, the character itself is a palindrome. The other base case is for two characters, and they are the same, like "bb".

As for inductive steps, count[i][j] = count[i+1][j-1] if count[i+1][j-1] is a palindrome and input.charAt(i) == input.charAt(j).

4. One important thing to note: since we care about count[i+1][j-1] before we need to care about count[i][j], we need to calculate count[i+1][j-1] first. Therefore, we need to calculate i from s.length() - 1 to 0, rather than from 0 to s.length() - 1

我的參考程式碼:

 1 class Solution {
 2     public String longestPalindrome(String s) {
 3         if(s.length() == 0){
 4             return "";
 5         }
 6         
 7         int[][] count = new int[s.length()][s.length()];
 8         for(int i = 0; i < s.length(); i++){
 9             for(int j = 0; j < s.length(); j++){
10                 if(i == j){
11                    count[i][j] = 1; 
12                 }else{
13                    count[i][j] = 0; 
14                 }
15                 
16             }
17         }
18         
19         int max = 1;
20         String maxS = s.charAt(0) + "";
21         for(int i = s.length() - 1; i >= 0; i--){
22             for(int j = i; j < s.length(); j++){
23                 if(s.charAt(i) == s.charAt(j)){
24                     if(i + 1 == j){
25                         count[i][j] = count[i][j-1] + 1;
26                     }else if(i == j){
27                         count[i][j] = 1;
28                     }else if(count[i+1][j-1] != 0){
29                         count[i][j] = count[i+1][j-1] + 2;
30                     }
31                     
32                     if(count[i][j] > max){
33                         max = count[i][j];
34                         maxS = s.substring(i, j+1);
35                     }
36                 }
37             }
38         }
39         return maxS;
40     }
41 }

 

另外一種dynamic programming的方法:

相比於我的方法,這個方法把2D array用來記錄boolean value。然後這個方法只有18行(超級短,是我的方法的一半)。但是,對於我現在的情況而言,程式碼的簡潔固然重要,不過對我來說,程式碼本身是否能跑 > 程式碼的效率 > 程式碼的可讀性 > 程式碼的短。這個只作為參考。然後下面是我對這個方法的分析:

1. 先建了一個儲存boolean value的2D array,但是沒有initialize。(這裡不會出現garbage value,因為像array這樣的data structure是儲存在heap裡面的。因為這個array是boolean,所以它有預設的值,是false。所以,如果在技術層面本身的問題方面,我們不用考慮它的initialize的問題。)

2. 然後也是用了一個nested loop。對於dp[i][j],在滿足s.charAt(i) == s.charAt(j),並且 i和j指向同一個 或者 i和j是相鄰 或者 dp[i+1][j-1]是palindrome的情況下,把dp[i][j]設定成true。

3. 在loop裡,如果當前格子是palindrome,並且當前的string長度大於之前存的string長度,把string替換成當前長度。

4. 等loop完之後,return 所儲存的最長的palindrome。

參考程式碼:

 1 public String longestPalindrome(String s) {
 2   int n = s.length();
 3   String res = null;
 4     
 5   boolean[][] dp = new boolean[n][n];
 6     
 7   for (int i = n - 1; i >= 0; i--) {
 8     for (int j = i; j < n; j++) {
 9       dp[i][j] = s.charAt(i) == s.charAt(j) && (j - i < 3 || dp[i + 1][j - 1]);
10             
11       if (dp[i][j] && (res == null || j - i + 1 > res.length())) {
12         res = s.substring(i, j + 1);
13       }
14     }
15   }
16     
17   return res;
18 }