最长回文子序列dp到底如何实现?怎么优化?

最长回文子序列的动态规划优化,你以为很简单?但真的是这样吗?

你以为最长回文子序列问题不过是个小小的动态规划问题,稍微动动脑子,随便写几行代码就能搞定?哈哈,太天真了!别急,先别走开,今天我就带你见识一下,如何通过动态规划玩出花样,还能把空间复杂度狠狠压缩,真正实现算法的优雅与高效

问题定义

给定一个字符串 s,需要找到 s 的最长回文子序列的长度。回文子序列是指正读和反读相同的子序列。

DP 基本思路

dp[i][j] 表示字符串 s 在区间 [i, j] 内的最长回文子序列长度。我们可以通过以下递推关系来构建 DP 表:

  1. 初始状态

    • i == j 时,dp[i][j] = 1,因为单个字符本身就是一个回文子序列。
    • i > j 时,dp[i][j] = 0,这种情况是非法的,不存在子序列。
  2. 状态转移方程

    • 如果 s[i] == s[j],那么 dp[i][j] = dp[i+1][j-1] + 2。这是因为 s[i]s[j] 相等,可以将它们包围在 dp[i+1][j-1] 的最长回文子序列的两端,形成新的回文子序列。
    • 如果 s[i] != s[j],那么 dp[i][j] = max(dp[i+1][j], dp[i][j-1])。此时只能选择丢弃其中一个字符,来寻找剩余部分的最长回文子序列。

你以为就是一张二维表?但真的是这样吗?

很多人都会第一时间想到二维DP表。是的,没错,我们可以通过一个 n * n 的表来存储子问题的解,甚至用 O(n^2) 的空间来轻松搞定问题。但等等,你真的以为这样就是最佳解法了吗?难道我们就甘心随便写写,浪费那么多宝贵的空间吗?作为一个技术追求完美的程序员,我们必须逼自己一把,把这玩意优化到极致

滚动数组,原来空间可以这么省!

现在,问题来了,怎么做才能既保持时间复杂度的高效,又把空间复杂度优化得更低呢?答案就是——滚动数组!没错,只需要两个简单的一维数组,你就可以让空间复杂度从 O(n^2) 降到 O(n),对,就是这么酷炫!

让我们深入看看这个骚操作。你还记得我们的DP表是如何更新的吗?是不是只用到了 dp[i+1][j-1]dp[i+1][j]dp[i][j-1]?嘿,这就是关键!我们其实只需要当前行和上一行的状态来推导新的一行。其他的?统统扔掉,根本不需要记!

这里就是具体的代码实现,准备好了吗?

int longestPalindromeSubseq(string s) {
    int n = s.size();
    vector<int> dp_curr(n, 0), dp_prev(n, 0);  // 两个一维数组

    for (int i = n - 1; i >= 0; --i) {
        dp_curr[i] = 1;  // 单个字符的回文长度为1
        for (int j = i + 1; j < n; ++j) {
            if (s[i] == s[j]) {
                dp_curr[j] = dp_prev[j - 1] + 2;  // 如果两边字符相等
            } else {
                dp_curr[j] = max(dp_curr[j - 1], dp_prev[j]);  // 否则取最大值
            }
        }
        dp_prev = dp_curr;  // 更新前一行
    }
    return dp_curr[n - 1];  // 返回最长回文子序列的长度
}

你看,就是这么简单的几行代码,却能让你在算法竞赛中迅速脱颖而出。注意到关键点了吗?每次我们计算 dp_curr[j] 的时候,只用关心上一行 dp_prev 的值和当前行 dp_curr 的前一个值。所有不需要的信息都被无情抛弃了!这就是空间优化的力量

如何理解这段代码?

为什么这段代码如此有效?深挖一下,我们可以发现:

  1. 初始状态:对于每个 idp_curr[i] 初始化为 1,表示每个单字符的回文长度。
  2. 双层循环:外层循环从字符串尾部向前遍历,内层从 i+1 开始向后遍历。这样可以保证每次计算 dp_curr[j] 时,前面的值都已经被正确更新。
  3. 状态转移:当 s[i] == s[j] 时,表示两边字符可以围成一个更长的回文,所以加上 dp_prev[j-1] 的值;否则,我们只能选择 dp_curr[j-1]dp_prev[j] 中较大的一个。

这段代码不仅逻辑清晰,而且极具扩展性。当你掌握了这个优化技巧,类似的DP问题,你都可以用类似的方法来进行空间优化。

学会了吗?那就赶紧实践一下!

到这里,很多人可能已经迫不及待地想去实践了!但我得提醒你,理论与实践结合,才是让你真正掌握这个优化技巧的唯一途径。下次再遇到类似的问题,别忘了这次的学习!这不仅仅是为了优化空间复杂度,而是为了让你在算法的世界里如鱼得水!

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值