LeetCode516 .最長迴文子序列
題目敘述:
力扣題目連結(opens new window)
給你一個字串 s
,找出其中最長的迴文子序列,並返回該序列的長度。
子序列定義為:不改變剩餘字元順序的情況下,刪除某些字元或者不刪除任何字元形成的一個序列。
示例 1:
輸入:s = "bbbab"
輸出:4
解釋:一個可能的最長迴文子序列為 "bbbb" 。
示例 2:
輸入:s = "cbbd"
輸出:2
解釋:一個可能的最長迴文子序列為 "bb" 。
提示:
1 <= s.length <= 1000
s
僅由小寫英文字母組成
動態規劃思路
-
我們在上文中已經介紹了迴文子串,那麼我們可以沿用
迴文子串
的思想解決這道題,但是我們首先得明確迴文子串
和迴文子序列
的區別 -
LeetCode647.迴文子串
求的是迴文子串,而本題要求的是迴文子序列, 要搞清楚這兩者之間的區別。 -
迴文子串是要連續的,迴文子序列可不是連續的! 迴文子串,迴文子序列都是動態規劃經典題目。
-
迴文子串,可以做這兩題:
-
647.迴文子串
-
5.最長迴文子串
-
思路其實是差不多的,但本題要比求迴文子串簡單一點,因為情況少了一點。
動規五部曲分析如下:
1.確定狀態變數及其含義
- 我們設立dp陣列,dp[i]] [j] 表示s字串在
[i,j]
範圍內最長迴文子序列的長度。(j
>=i
) - 那麼我們確立了狀態變數
dp[i][j]
,那麼我們就要開始處理遞推公式和如何初始化了
2.確定遞推公式
- 在這裡,我們最重要的就是判斷
s[i],s[j]
之間的關係s[i]==s[j]
此時,dp[i][j]=dp[i+1][j-1]+2
- 為什麼是+2呢?因為本題是最長迴文子序列,當
s[i]==s[j]
時,[i,j]
範圍內至少有dp[i+1][j-1]+2
這個大小的最長迴文子序列,+2就是加上s[i],s[j]
這兩個字元。
- 如果
s[i]與s[j]不相同
,說明s[i]和s[j]
的同時加入 並不能增加[i,j]
區間迴文子序列的長度,那麼分別加入s[i]、s[j]
看看哪一個可以組成最長的迴文子序列。
加入s[j]
的迴文子序列長度為dp[i + 1] [j]
。
加入s[i]
的迴文子序列長度為dp[i] [j - 1]
。
那麼dp [i] [j]
一定是取最大的,即:dp [i] [j] = max(dp [i + 1] [j], dp[i] [j - 1])
;
3.如何初始化dp陣列
- 首先,我們得處理特殊情況,當
i==j
的時候,這個時候在[i,j]
範圍內只有一個字元,使用dp[i][j]=dp[i+1][j-1]+2
會導致當前處理的子串的左邊界大於右邊界,此時我們就得特殊處理一下,當處理的子串只有一個字元時,i==j
,並且dp[i][j]
顯然等於1,因為單個字元也是迴文子序列,並且這個迴文子序列的長度是1。
vector<vector<int>> dp(s.size(), vector<int>(s.size(), 0));
for (int i = 0; i < s.size(); i++) dp[i][i] = 1;
4. 確定遍歷順序
從遞迴公式中,可以看出,dp[i][j]
依賴於 dp[i + 1][j - 1]
,dp[i + 1][j] 和 dp[i][j - 1]
,如圖:
- 所以說我們想要得到
dp[i][j]
,必須從左下方開始,向著右上方的方向進行遞推。 - 所以說遍歷順序就是從下到上,從左到右
//開始對dp陣列進行從下到上,從左到右進行賦值。
for(int i=s.size()-1;i>=0;i--){
for(int j=i+1;j<s.size();j++){
if(s[i]==s[j]) dp[i][j]=dp[i+1][j-1]+2;
else dp[i][j]=max(dp[i][j-1],dp[i+1][j]);
}
}
5.舉例列印dp陣列
輸入s:"cbbd" 為例,dp陣列狀態如圖:
紅色框即:dp[0][s.size() - 1];
為最終結果。
最終程式碼:
//最長迴文子序列
class Solution {
public:
int longestPalindromeSubseq(string s) {
//建立二維的dp陣列
vector<vector<int>> dp(s.size(),vector<int>(s.size(),0));
//初始化dp陣列,首先要將i和j相等的時候,也就是隻有一個字元的子序列,它的dp值賦值為1
for(int i=0;i<s.size();i++) dp[i][i]=1;
//開始對dp陣列進行從下到上,從左到右進行賦值。
for(int i=s.size()-1;i>=0;i--){
for(int j=i+1;j<s.size();j++){
if(s[i]==s[j]) dp[i][j]=dp[i+1][j-1]+2;
else dp[i][j]=max(dp[i][j-1],dp[i+1][j]);
}
}
//最後,從0-s.size()-1這個範圍的最長迴文子序列的長度就是我們需要的答案。
return dp[0][s.size()-1];
}
};
註明
- 本文中引用了作者
程式碼隨想錄
的部分圖片和原文,若想深入瞭解,可以去原作者的文章閱讀 - 程式碼隨想錄