LeetCode516. 最長迴文子序列

RJ_theMag發表於2020-11-12

經典動態規劃問題。用dp[i][j]表示字串s的以i開頭,以j結尾的子串的最大回文子序列的長度。我們要求的s的最長迴文子序列的長度就是dp[0][n - 1]。

考慮一下陣列的初始化,對於所有的i(0 <= i < n),都有dp[i][i] = 1,表示單個字母可以組成一個長度為1的迴文子序列。

然後要進行遞推和狀態轉移,我們列舉子串的長度len從2到n,子串的起點i從0到n - len + 1。n - len + 1是為了讓當前子串的結尾不超過s的長度。
這樣我們得到當前子串的起點i,和終點j = n - len + 1。

比較s[i]與s[j]是否相等。
(1)如果相等,說明子串s[i ~ j]可以在原來的子串s[i + 1 ~ j - 1]的基礎上構成
一個長度+2的迴文子序列。因此我們得到狀態轉移方程:dp[i][j] = dp[i + 1][j - 1] + 2
(2)如果不相等,說明子串s[i ~ j]無法在原來的子串s[i + 1 ~ j - 1]的基礎上構成
一個長度+2的迴文子序列,但是s[i + 1 ~ j]和s[i ~ j - 1]仍然有可能是一個迴文子序列,我們要求的s[i ~ j]的最大回文子序列肯定是
s[i + 1 ~ j]和s[i ~ j - 1]兩者中的最大回文子序列的較大者。因此我們得到狀態轉移方程:dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])

列舉完長度和子串起點之後,最後返回的dp[0][n - 1]就是答案。

程式碼如下:

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        int n = s.size();
        vector<vector<int>> dp(n, vector<int>(n, 0));
        for(int i = 0; i < n; ++i) {
            dp[i][i] = 1;
        }
        for(int len = 2; len <= n; ++len) {
            for(int i = 0; i + len - 1 < n; ++i) {
                int j = i + len - 1;
                if(s[i] == s[j]) {
                    dp[i][j] = dp[i + 1][j - 1] + 2;
                } else {
                    dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
                }
            }
        }
        return dp[0][n - 1];
    }
};

相關文章