Leetcode 516. 最长回文序列 区间dp C++实现

Leetcode 516. 最长回文序列

问题:给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。

子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。

算法1:记忆化搜索

        双指针,left 指针指向最左边,right 指针指向最右边,当 s [ left ] == s [ right ] 时,回文数 + 2 ,两者不相等时,左指针 left 右移或者右指针 right 左移。

时间复杂度:O(n²) 。

        其中 ns 的长度。动态规划的时间复杂度 = 状态个数 × 单个状态的转移个数。本题中状态个数等于 O(n²),而单个状态的转移个数为 O(1),因此时间复杂度为 O(n²)

空间复杂度:O(n²) 。

        保存多少状态,就需要多少空间。

代码:

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        int n = s.length();
        vector<vector<int>> memo(n,vector<int>(n,-1));
        auto dfs = [&](auto &&dfs,int left,int right) -> int{
            if(left > right)    return 0;
            if(left == right)   return 1;
            int& res = memo[left][right];
            if(res != -1)   return res;
            if(s[left] == s[right]) return dfs(dfs,left + 1,right - 1) + 2;
            return max(dfs(dfs,left + 1,right),dfs(dfs,left,right - 1));
        };
    return dfs(dfs,0,n - 1);
    }
};

算法2:1:1 翻译成递推

        如何思考循环顺序?什么时候要正序,什么时候要倒序?

        这里有一个通用的做法:盯着状态转移方程,想一想,要计算 dp [ i ] [ j ] ,必须先把 dp [ i + 1 ] [ ⋅ ] 算出来,那么只有 i 从大到小枚举才能做到。对于 j 来说,由于在计算 dp [ i ] [ j ] 的时候,需要用到 dp [ i ] [ j − 1 ] ,也就是必须先把 dp [ i ] [ j − 1 ] 算出来,所以 j 必须从小到大枚举。

时间复杂度:O(n²) 。

        其中 n s 的长度。动态规划的时间复杂度 = 状态个数 × 单个状态的转移个数。本题中状态个数等于 O(n²),而单个状态的转移个数为 O(1),因此时间复杂度为 O(n²)

空间复杂度:O(n²) 。

代码:

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

算法3:空间优化

        把 dp 数组的第一个维度去掉。相当于把 dp [ i ] dp [ i + 1 ] 保存到同一个一维数组中。

但一个萝卜一个坑,dp [ j − 1 ] 要么保存的是 dp [ i + 1 ] [ j −  1 ] ,要么保存的是 dp [ i ] [ j − 1 ] ,怎么妥当地处理新旧数据?对于本题来说,可以用变量 pre 记录 dp [ i + 1 ] [ j  − 1 ] 的值。计算到 dp [ j ] 时,dp [ j − 1 ] 保存的是新数据 dp [ i ] [ j − 1 ],旧数据 dp [ i + 1 ] [ j − 1 ] 可以从 pre 中取到。

时间复杂度:O(n²) ,其中 为 s 的长度。

空间复杂度:O(n) 

代码:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值