5. Longest Palindromic Substring
最長回文子串 (Longest Palindromic Substring) 是常考題。Palindrome 就是正讀反讀都一樣的詞語,比如範例給的 “bab”、 “bb” ,實際單字如 “level” 等等都屬於它。因為較好的解法是 DP 類型,初見就能想到,難度也比較高。一般人能熟悉 Dynamic Programming - 2D matrix 解就好了。(看過令人膜拜的神解 Manacher’s Algorithm,時間複雜度提升到了 O(n) …)
思路
我們維護一個 2D-Matrix 動態紀錄子問題狀態,其中 dp[i][j]
表示字符串區間 [i, j]
是否為回文串。明顯地,當 i = j
時,表示只有一個字符,肯定是 Palindrome ,故先把對角線表填完 T (如上圖) 。
再來是 Palindromic 字串的特性,若字串最兩端點一樣,則 dp[i+1][j-1]
表示內縮一層的單字,
dp[i][j] = dp[i + 1][j - 1];
再來特別注意 Palindromic 字串在 DP 表格之間的依賴關係很特別,故for loop 時候必須:
// 特別注意 i,j 寫法及順序!!
for(int j = 1 ; j < length ; j++){
for(int i = 0; i < j ; i++){
...
}
}
for(int i = length - 1 ; i >= 0 ; i--){
for(int j = length - 1; j >= i ; j--){
...
}
}
因為在判斷 dp[i][j]
時,需要先有 dp[i+1][j-1]
的資訊,要不然會出錯 ! 錯誤的原因如下圖箭頭處所表示。
- 順序逐 row 計算會出錯
解答
class Solution {
public String longestPalindrome(String s) {
if ( s.length() == 1 ) {
return s;
}
String ans = s.substring( 0, 1 );
int maxLength = 1;
int length = s.length();
boolean[][] dp = new boolean[length][length];
for ( int i = 0; i < length; i++ ) {
dp[i][i] = true;
}
for ( int j = 1; j < length; j++ ) {
for ( int i = 0; i < j; i++ ) {
if ( s.charAt( i ) == s.charAt( j ) ) {
if ( j - i < 2 ) {
dp[i][j] = true;
} else {
dp[i][j] = dp[i + 1][j - 1];
}
}
if ( dp[i][j] && j - i + 1 > maxLength ) {
maxLength = j - i;
ans = s.substring( i, j + 1 );
}
}
}
return ans;
}
j-i <= 2
的理由是因為前面判斷了 s.charAt(i) != s.charAt(j)
,故子字串只要長度小於等於 2 ,就一定是回文。再來只要 dp[i][j] == true
,就表示 s.substring(i, length)
是回文。其中長度是 j-i+1 。
時間空間複雜度
時間複雜度 : O(N^2)
兩層迴圈
空間複雜度 : O(N^2)
2-D矩陣,為了紀錄某步驟執行結果(動態規劃)
Vocabulary
palindrome [ˋpælɪn͵drom] (只會出現在考題的單字,記得怎麼念就好)
n.[C] 回文字