当前位置: 首页 > article >正文

【LeetCode】动态规划—516. 最长回文子序列(附完整Python/C++代码)

动态规划—516. 最长回文子序列

  • 前言
  • 题目描述
  • 基本思路
    • 1. 问题定义
    • 2. 理解问题和递推关系
    • 3. 解决方法
      • 3.1 动态规划方法
      • 3.2 空间优化的动态规划
    • 4. 进一步优化
    • 5. 小总结
  • 代码实现
    • Python
      • Python3代码实现
      • Python 代码解释
    • C++
      • C++代码实现
      • C++ 代码解释
  • 总结:

前言

最长回文子序列 问题是动态规划的重要应用之一。在实际生活中,很多信息都可以通过回文子序列来进行抽象和处理。本题旨在通过动态规划的方法,帮助我们有效地找到字符串中的最长回文子序列。

本文将详细阐述该问题的思路,包括如何定义状态、推导递推关系,并提供 Python 和 C++ 的实现代码,帮助读者更深入地理解动态规划的应用。

题目描述

在这里插入图片描述

基本思路

1. 问题定义

给定一个字符串 s s s ,求出该字符串的最长回文子序列的长度。回文子序列是指在字符串中删除一些字符(可以是零个或多个),使得剩下的字符能够组成一个回文字符串。

2. 理解问题和递推关系

  • 回文子序列的特性是从两端向中间对称。若字符串的首尾字符相同,那么这两个字符都可以作为回文子序列的一部分。
  • 定义 d p [ i ] [ j ] d p[i][j] dp[i][j] 为子串 s [ i … j ] s[i \ldots j] s[ij] 的最长回文子序列的长度。
  • 递推关系:
    • 如果 s [ i ] = = s [ j ] s[i]==s[j] s[i]==s[j] ,那 d p [ i ] [ j ] = d p [ i + 1 ] [ j − 1 ] + 2 d p[i][j]=d p[i+1][j-1]+2 dp[i][j]=dp[i+1][j1]+2
    • 如果 s [ i ] ! = s [ j ] s[i]!=s[j] s[i]!=s[j], 那 / d p [ i ] [ j ] = \ max ⁡ ( d p [ i + 1 ] [ j ] , d p [ i ] [ j − 1 ] ) / d p[i][j]=\backslash \max (d p[i+1][j], d p[i][j-1]) /dp[i][j]=\max(dp[i+1][j],dp[i][j1])
  • 边界条件:当 i = = j i==j i==j 时,单个字符的最长回文子序列长度为 1

3. 解决方法

3.1 动态规划方法

  1. 创建一个二维数组 d p d p dp ,其大小为 n × n n \times n n×n ,其中 n n n 是字符串的长度。
  2. 初始化对角线上的元素为 1 ,表示每个单独字符的回文子序列长度为 1
  3. 使用两个嵌套循环填充 d p d p dp 数组,从长度 2 开始,直到 n n n:计算毎一对 (i, j) 的回文子序列长度。
  4. 最终结果存储在 d p [ 0 ] [ n − 1 ] d p[0][n-1] dp[0][n1] 中,即整个字符串的最长回文子序列长度。

3.2 空间优化的动态规划

  • 由于 d p [ i ] [ j ] d p[i][j] dp[i][j] 只依赖于 d p [ i + 1 ] [ j ] d p[i+1][j] dp[i+1][j] d p [ i ] [ j − 1 ] d p[i][j-1] dp[i][j1] ,可以将空间复杂度优化到 O ( n ) O(n) O(n) ,只使用一维数组进行计算。

4. 进一步优化

  • 空间复杂度优化:利用—维数组相代二维数组,减少内存占用。
  • 时间复杂度:动态规划的时间复杂度为 O ( n ∧ 2 ) O\left(n^{\wedge} 2\right) O(n2) ,可以处理长度适中的字符串。

5. 小总结

  • 动态规划提供了一种有效的方法来解决最长回文子序列的问题,能够通过状态转移找到全局最优解。
  • 通过对 d p d p dp 数组的优化,可以大幅降低空间复杂度,使得算法在处理大规模输入时依然高效。
  • 这个问题是动态规划中的经典案例,掌握其解法对于解决更复杂的回文问题具有重要意义。

以上就是最长回文子序列问题的基本思路。

代码实现

Python

Python3代码实现

class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
        n = len(s)
        # 创建dp数组
        dp = [[0] * n for _ in range(n)]

        # 单个字符的回文子序列长度为1
        for i in range(n):
            dp[i][i] = 1
        
        # 填充dp数组
        for length in range(2, n + 1):  # 子串长度从2到n
            for i in range(n - length + 1):
                j = i + length - 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]

Python 代码解释

  • 初始化:创建一个二维数组 dp 并初始化每个单个字符的回文子序列长度为 1
  • 填充 dp 数组:使用两层循环计算各个子串的回文子序列长度,依据首尾字符的比较进行递推。
  • 返回结果:最终返回 dp[0][n-1],即整个字符串的最长回文子序列长度。

C++

C++代码实现

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        int n = s.size();
        // 创建dp数组
        vector<vector<int>> dp(n, vector<int>(n, 0));

        // 单个字符的回文子序列长度为1
        for (int i = 0; i < n; i++) {
            dp[i][i] = 1;
        }

        // 填充dp数组
        for (int length = 2; length <= n; length++) {  // 子串长度从2到n
            for (int i = 0; i <= n - length; i++) {
                int j = i + length - 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];
    }
};

C++ 代码解释

  • 初始化:创建一个二维 dp 数组并设置每个单个字符的回文子序列长度为 1
  • 动态规划填充:使用双重循环遍历每个可能的子串,根据字符相同与否更新 dp 数组。
  • 返回结果:返回 dp[0][n-1],表示整个字符串的最长回文子序列的长度。

总结:

  • 动态规划是解决最长回文子序列问题的有效工具,能够通过状态转移找到最优解。
  • 通过合理的状态设计与空间优化,可以在保持效率的前提下大幅度降低空间复杂度。
  • 掌握此问题的解决方法,不仅对理解动态规划有很大帮助,也为其他复杂回文问题的处理奠定基础。

http://www.kler.cn/news/338928.html

相关文章:

  • ffmpeg源码分析(七)结构体之AVStream
  • day03 笔试练习
  • LeetCode讲解篇之322. 零钱兑换
  • 每天五分钟深度学习pytorch:基于pytorch搭建一元线性回归模型
  • Axure中文版:原型设计新手必备工具,轻松上手!
  • 数据挖掘笔记part one (认识数据挖掘)
  • matplotlib中文显示乱码问题
  • 线性代数书中求解齐次线性方程组、非齐次线性方程组方法的特点和缺陷(附实例讲解)
  • 在 JavaScript 中使用 window 对象来刷新页面
  • 深入浅出:现代JavaScript开发者必知必会的Web性能优化技巧
  • 体系结构论文(五十四):Reliability-Aware Runahead 【22‘ HPCA】
  • 简单认识redis-5 jdbc 与 jedis 使用的区别
  • Redis 缓存策略详解:提升性能的四种常见模式
  • springboot整合mybatis案例
  • Spring Boot助力医院数据管理
  • 【C语言】关于指针各项细节以及与其他知识点关联
  • Java Web 开发
  • 目标检测 DN-DETR(2022)
  • 数据库分区
  • tensorflow快速入门--如何定义张量、定义网络结构、超参数设置、模型训练???