線性dp:LeetCode516 .最長迴文子序列

Tomorrowland_D發表於2024-09-07

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]這兩個字元。

516.最長迴文子序列

  • 如果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]);

516.最長迴文子序列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],如圖:

img

  • 所以說我們想要得到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陣列狀態如圖:

516.最長迴文子序列3

紅色框即: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];
    }
};

註明

  • 本文中引用了作者程式碼隨想錄的部分圖片和原文,若想深入瞭解,可以去原作者的文章閱讀
  • 程式碼隨想錄

相關文章