程式設計學習筆記(LeetCode-516. 最長迴文子序列)
阿新 • • 發佈:2021-08-15
程式設計學習筆記(LeetCode-516. 最長迴文子序列)
<516>最長迴文子序列
- 問題重述:
設有以字串 \(s\) ,找出其最長迴文子序列,並且返回該序列的長度。
子序列: 不改變剩餘字元順序的情況下,刪除某些字元或者不刪除任何字元形成的一個序列。
迴文: 即是正讀和反讀都一樣的字串(例如:aba,abba,bbbb等)。
注: 單個字元也被認為是一個迴文串;
示例1:
輸入: s = "bbbab"
輸出: 4
解釋: 一個可能的最長迴文子序列為 "bbbb" 。
示例2:
輸入: s = "cbbd"
輸出: 2
解釋: 一個可能的最長迴文子序列為 "bb" 。
限定:
\(1 \leqslant s.length \leqslant 1000\)
\(s\) 僅由小寫英文字母組成
解析與求解(動態規劃)
- 對於一個子序列,若它是迴文子序列,且長度大於 2 ,去其首位,其仍是一個迴文子序列。
- 若用 \(dp[i][j]\) 表示 \(s[i,j]\) 內最長的迴文子串長度,且 \(n\) 為字串的長度,那麼:
- 由於任何長度為1的子序列都是迴文子序列,那麼:
- 當 \(i<j\) 時,需要分別考慮 \(s[i]=s[j]\) 和 \(s[i]\neq s[j]\) 的情況。
當 \(s[i]=s[j]\) 時:\[dp[i][j]=dp[i+1][j-1]+2 \]當 \(s[i] \neq s[j]\) 時:\[dp[i][j]=max(dp[i+1][j],\ dp[i][j-1]) \]
dp計算順序:
- 我們想要的結果最終會落在 \(dp[0][n-1]\) 處。
- 狀態轉移方程都是從長度較短的子序列向長度較長的子序列轉移。
那麼dp的計算順序為:
從矩陣的右下角開始計算,每行從\((dp[i][j],\ i=j)\)
C++程式碼實現:
class Solution {
public:
int longestPalindromeSubseq(string s) {
//建立動態規劃陣列dp
vector<vector<int> > dp = vector<vector<int> >(s.size(), vector<int>(s.size(), 0));
for(int i = s.size() - 1; i >= 0; i--){
for(int j = i; j < s.size(); j++){
if(i > j){continue;} //0 <= i <= j <= n
//長度為1的字元為迴文串
else if(i == j){dp[i][j] = 1;}
//提示: 當是s[i]=s[j]且i和j相鄰的時候,
// 那麼i+1=j,j-1=i,dp[i+1][j-1]即為dp[i][j]的對稱點。
// 由於dp[i][j]的對稱點值為0,所以長度恰好是0+2
else if(s[i] == s[j]){dp[i][j] = dp[i+1][j-1]+2;}
//當首尾字元不相等的時候,返回去掉當前的首或者當前的尾後的最長迴文子序列
else if(s[i] != s[j]){dp[i][j] = max(dp[i][j-1], dp[i+1][j]);}
}
}
//返回最終結果
return dp[0][s.size()-1];
}
};
時空複雜度:
- 時間複雜度: \(O(n^2),\ n\)為字串 \(s\) 的長度。
- 空間複雜度: \(O(n^2),\ n\)為字串 \(s\) 的長度。
備註: 摘錄自LeetCode官方題目解析。