LeetCode:516 最长回文子序列

典型的动态规划题,可以用递归、迭代两种形式来解。

 

首先引用一篇讲子序列问题的好文章:子序列问题通用思路|最长回文子序列

以下内容引自上文:

子序列问题是常见的算法问题,而且并不好解决。

首先,子序列问题本身就相对子串子数组更困难一些,因为前者是不连续的序列,而后两者是连续的,就算穷举你都不一定会,更别说求解相关的算法问题了。

而且,子序列问题很可能涉及到两个字符串,比如前文「最长公共子序列」,如果没有一定的处理经验,真的不容易想出来。所以本文就来扒一扒子序列问题的套路,其实就有两种模板,相关问题只要往这两种思路上想,十拿九稳。

一般来说,这类问题都是让你求一个最长子序列,因为最短子序列就是一个字符嘛,没啥可问的。一旦涉及到子序列和最值,那几乎可以肯定,考察的是动态规划技巧,时间复杂度一般都是 O(n^2)。

原因很简单,你想想一个字符串,它的子序列有多少种可能?起码是指数级的吧,这种情况下,不用动态规划技巧,还想怎么着?

既然要用动态规划,那就要定义 dp 数组找状态转移关系。我们说的两种思路模板,就是 dp 数组的定义思路。不同的问题可能需要不同的 dp 数组定义来解决。

 

 解法一:用记忆化的递归求解。思路应该没问题,但是对于pair的hash一直报错,参数不匹配。这个问题暂时没有解决。

参考链接:自定义哈希函数

struct pair_hash{
    template<class T1, class T2>
    std::size_t operator() (const std::pair<T1, T2>& p) const{
        auto h1 = std::hash<T1>{}(p.first);
        auto h2 = std::hash<T2>{}(p.second);
        return h1 ^ h2;
    }
};

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        if(s.length() < 2) return 0;

        return curLongest(s, 0, s.length()-1);
    }
private:
    unordered_map <pair<int, int>, int, pair_hash> cache;

    int curLongest(string& s, int left, int right) { //[left, right] 闭区间
        if(left >= right) return 0;
        if(cache.count(pair<left, right>)) {
            return cache[pair<left, right>];
        }

        int longest;
        if(s[left] == s[right]) {
            longest = 2 + curLongest(left+1, right-1);
        }
        else {
            longest = max(curLongest(left+1, right), curLongest(left, right+1));
        }
        cache.insert(pair<left, right>, longest);
        return longest;
    }
};

解法二:Bottom - Up 解法

注意:初始化是容易出错的地方,只有对角线应该初始化成1,对应的实际意义是自己到自己的闭区间,就是字符本身,形成的回文序列长度是1。

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        if(s.length() < 1) return 0;

        int n = s.length();
        vector<vector<int>> dp (n, vector<int> (n, 0)); // n*n矩阵,初始化为0,初始化为1不对!

        for (int i = n-1; i >= 0; i--) {
            dp[i][i] = 1; //对角线元素为1,代表自身长度为1
            for (int j = i+1; j < n; j++) { //i=n-1时,j=n,不会进入循环因此不会越界,因此不必padding
                if(s[i] == s[j]) {
                    dp[i][j] = 2 + dp[i+1][j-1];
                }
                else {
                    dp[i][j] = max(dp[i+1][j], dp[i][j-1]);
                }
            }
        }

        return dp[0][n-1];
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值