【算法 - 动态规划】最长回文子序列

上篇文章中,我们学习一个新的模型: 样本对应模型,该模型的套路就是:以结尾位置为出发点,思考两个样本的结尾都会产生哪些可能性

而前篇文章中的 纸牌博弈问题 属于 [L , R]上范围尝试模型。该模型给定一个范围,在该范围上进行尝试,套路就是 思考 [L ,R] 两端该如何取舍。

本篇文章我们通过一道中等难度的力扣题,再来熟悉 范围尝试模型 的套路,注意与 上篇文章的最长公共子序列 作对比哦!

力扣516. 最长回文子序列

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

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

示例 1:

输入: s = “bbbab”

输出: 4

解释: 一个可能的最长回文子序列为 “bbbb” 。

示例 2:

输入: s = “cbbd”

输出: 2

解释: 一个可能的最长回文子序列为 “bb” 。

学会了 上篇 的 最长公共子序列 之后,这道题目就有一个讨巧的方法:

若翻转输入的字符串,那么原本的字符串与翻转后的字符串的 最长公共子序列 就是 最长回文子序列

只需稍加修改,就能套用上面的代码了!

代码

public static int palindromeSubsequence(String s) {char[] s1 = s.toCharArray();int N = s1.length;char[] s2 = new char[N];// 翻转 s 字符串for (int i = 0; i < N; i++) {s2[N - i - 1] = s1[i];}// 调用 判断最长公共子序列 的动态规划函数return longestCommonSubsequence(s1,s2);
}// 该函数是 上篇文章末尾 的 动态规划版 
public static int longestCommonSubsequence(char[] str1, char[] str2) {int N = str1.length;int M = str2.length;...
}

除了上面讨巧的办法外,我们依然采用最朴素的 暴力递归 来思考这道题目。

递归的准备

定义递归函数的功能: 返回 str 中 [L ... R] 范围上字符串的最长回文子序列。

思考递归需要的参数: str 字符串,两端范围 L, R。

明确递归的边界条件:

  • 当字符串长度为 1 即 L == R 时,找到了一个长度为 1 的回文序列,返回 1 。
  • 当字符串长度为 2 即 L == R - 1 时,若两个字符一致,即找到了一个长度为 2 的回文序列,返回 2 。否则返回 1。

思路

这道题就是典型的 范围尝试模型 ,因此,递归就可以按照 开头和结尾两端都会产生哪些可能性 的思路来划分情况:

  • 回文子序列 既不以 L 结尾,也不以 R 结尾;
  • 回文子序列 L 结尾,不以 R 结尾;
  • 回文子序列 不以 L 结尾, R 结尾;
  • 回文子序列 既以 L 结尾,也以 R 结尾。

因为要求 最长 回文子序列,因此要返回这四种情况当中的最大值。

代码

public static int palindromeSubsequence(String s) {if (s == null || s.length() == 0) {return 0;}char[] str = s.toCharArray();return process(str, 0, str.length - 1);
}public static int process(char[] str, int L, int R) {if (L == R) {return 1;}if (L == R - 1) {return str[L] == str[R] ? 2 : 1;}int p1 = process(str, L + 1, R - 1);int p2 = process(str, L, R - 1);int p3 = process(str, L + 1, R);int p4 = str[L] != str[R] ? 0 : (2 + process(str, L + 1, R - 1));return Math.max(Math.max(p1, p2), Math.max(p3, p4));
}

代码解释

注意: 第四种情况 p4 中,如果直接调用 process(str, L, R),则会产生死循环。为使递归正常运行,要将都以 LR 结尾单独拿出来判断,若相等,去掉两端相等的字符再进行递归调用,回文子序列的长度自然要加上两端 2 的长度。


下面我们通过画 dp 表,探寻该递归如何转化为更加优化的动态规划。

以 str = “abcb1a” 为例。

可变的参数有两个,判断长度范围的 LR。因此,需要设置一个二维的 dp 表数组,由于 L, R 的取值范围从 0 开始到字符串长度减一,因此数组大小设置为 dp[N][N]

根据递归函数中代码逻辑发现:

  1. 当字符串中仅剩一个字符时,回文长度为 1 ,其余均为 0。
    if (L == R) {return 1;}

因此 dp 数组对角线上的数值均为 1 。

  1. 当字符串长度为 2 ,若两个字符一致,即找到了一个长度为 2 的回文序列,返回 2 。否则返回 1。
    if (L == R - 1) {return str[L] == str[R] ? 2 : 1;}

  1. 普遍情况下,依赖 左,下,左下 三个地方的 最大值
    int p1 = process(str, L + 1, R - 1);int p2 = process(str, L, R - 1);int p3 = process(str, L + 1, R);int p4 = str[L] != str[R] ? 0 : (2 + process(str, L + 1, R - 1));return Math.max(Math.max(p1, p2), Math.max(p3, p4));


  1. 范围尝试模型的 dp 表最大特点是左下角无效。递归函数调用的是 process(str, 0, str.length - 1),因此最终答案应该取 dp[0][N-1]== 5。

动态规划代码

public static int palindromeSubsequence(String s) {if (s == null || s.length() == 0) {return 0;}if (s.length() == 1) {return 1;}char[] str = s.toCharArray();int N = str.length;int[][] dp = new int[N][N];dp[N - 1][N - 1] = 1;// 单独填写对角线和相邻斜线的值for (int i = 0; i < N - 1; i++) {dp[i][i] = 1;dp[i][i + 1] = str[i] == str[i + 1] ? 2 : 1;}// 从下往上 从左往右 找最大值填写// 递归中的 p1 情况不需要考虑了for (int i = N - 3; i >= 0; i--) {for (int j = i + 2; j < N; j++) {dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]);if (str[i] == str[j]) {dp[i][j] = Math.max(dp[i][j], dp[i + 1][j - 1] + 2);}}}return dp[0][N - 1];
}

代码解释

一图胜千言:

在递归版本中,红色分别依赖紫、蓝、黄,并 取最大值

    int p1 = process(str, L + 1, R - 1);int p2 = process(str, L, R - 1);int p3 = process(str, L + 1, R);int p4 = str[L] != str[R] ? 0 : (2 + process(str, L + 1, R - 1));return Math.max(Math.max(p1, p2), Math.max(p3, p4));

思考一下,紫色部分的值是怎么来的?它也同样依赖了左,下,左下 三个地方的 最大值。因此,紫色 部分一定不小于 蓝色 部分,同理 黄色 部分也一定不小于 蓝色 部分。
因为要求最大值,因此可以忽略掉蓝色部分 p1 的递归调用,只需考虑 p2, p3 的调用。

    dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]);if (str[i] == str[j]) {dp[i][j] = Math.max(dp[i][j], dp[i + 1][j - 1] + 2);}

因此在动态规划循环中排除了蓝色部分的情况,先求出紫色与黄色的最大值,只有当 str[L] == str[R] 时,再与蓝色部分比出最大值。

这就是使用 严格表依赖 的好处 —— 可以 做进一步的优化

总结

上篇文章和本文分别讲解了 最长公共子序列问题最长回文子序列问题 。看似很相似的题目,实际使用了两种不同的模型:样本对应模型范围尝试模型 。根据不同模型的套路相信小伙伴也能够轻松应对类似的题目了!

下篇文章我们将介绍一个综合性非常高的题目,并使用一个新的模型来应对,敬请期待吧~

~ 点赞 ~ 关注 ~ 不迷路 ~!!!

------------- 往期回顾 -------------
【算法 - 动态规划】最长公共子序列问题
【算法 - 动态规划】力扣 691. 贴纸拼词
【算法 - 动态规划】原来写出动态规划如此简单!
【算法 - 动态规划】从零开始学动态规划!(总纲)
【堆 - 专题】“加强堆” 解决 TopK 问题!
AC 此题,链表无敌!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2799439.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

初阶数据结构之---顺序表和链表(C语言)

引言-线性表 线性表&#xff1a; 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构。线性表在逻辑上是线性结构&#xff0c;也就是说是连续的一条直线。但在物理上并不一定是连续的。线性表在物理上…

UE4 C++联网RPC教程笔记(三)(第8~9集)完结

UE4 C联网RPC教程笔记&#xff08;三&#xff09;&#xff08;第8~9集&#xff09;完结 8. exe 后缀实现监听服务器9. C 实现监听服务器 8. exe 后缀实现监听服务器 前面我们通过蓝图节点实现了局域网连接的功能&#xff0c;实际上我们还可以给项目打包后生成的 .exe 文件创建…

【服务器】服务器推荐

一、引言 在数字世界的浪潮中&#xff0c;服务器作为数据存储和处理的基石&#xff0c;其重要性不言而喻。而在这个繁星点点的市场中&#xff0c;雨云以其独特的优势和超高的性价比&#xff0c;逐渐成为众多企业和个人的首选。今天&#xff0c;就让我带你走进雨云的世界&#…

JAVA高并发——单例模式和不变模式

文章目录 1、探讨单例模式2、不变模式 由于并行程序设计比串行程序设计复杂得多&#xff0c;因此我强烈建议大家了解一些常见的设计方法。就好像练习武术&#xff0c;一招一式都是要经过学习的。如果自己胡乱打&#xff0c;效果不见得好。前人会总结一些武术套路&#xff0c;对…

关于 AC 自动机

什么是 AC 自动机 AC 自动机&#xff0c;全称 Aho-Corasick 自动机&#xff0c;是一种用于字符串搜索的算法&#xff0c;由 Alfred V. Aho 和 Margaret J. Corasick 在 1975 年提出。这个算法是为了解决在一个主文本字符串中查找多个模式字符串&#xff08;或称为“关键词”&a…

吴恩达机器学习全课程笔记第三篇

目录 前言 P42-P48 神经元和大脑 神经网络中的层 更复杂的神经网络 前向传播&#xff08;做出预测&#xff09; P49-P53 代码中的推理 构建一个神经网络 P54-P60 矩阵乘法 TensorFlow框架实现神经网络 前言 这是吴恩达机器学习笔记的第三篇&#xff0c;第二篇笔记…

绿盾限制终端网络访问权限会恢复后,别的网站访问正常就是无法访问钉钉网站和下载东西

环境&#xff1a; Win10 专业版 钉钉7.5.5 绿盾7.0 问题描述&#xff1a; 绿盾限制终端网络访问权限会恢复后&#xff0c;别的网站访问正常就是无法访问钉钉网站和下载东西 解决方案&#xff1a; 排查方法 1.重置浏览器或者更换浏览器测试&#xff08;未解决&#xff09…

nc开发刚导入项目eclipse出现莫名其妙的错误,红叉,感叹号,文件missing

解决类出现红叉 解决感叹号&#xff0c;文件missing 其他问题 右上角的视图&#xff0c;要选择java&#xff0c;如果是javaEE也会有一些文件没有展示出来。

QYWX企业微信的公告信息限制保存pdf的破解

公司使用企业微信好几年&#xff0c;重大的消息使用公告信息这个模块。可重要的消息无法保存&#xff0c;只能在线收藏。这个玩意只考虑到了维护企业利益&#xff0c;无视员工利益。 后来发现可以利用windows的虚拟打印机&#xff0c;将公告打印成pdf。 用了一段时间&#xf…

IOT-Reaserch安装ghidra以及IDEA和ghidra的配置

Linux research 5.4.0-91-generic #102~18.04.1-Ubuntu SMP Thu Nov 11 14:46:36 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux java --version IOT自带的java是符合要求的&#xff0c;不需要额外下载 iotresearch:~/install-file$ java --version openjdk 11.0.13 2021-10-19 …

【Quasar】quasar轮播图进度条

效果 开始效果 即将结束 结束 码 <template><q-carouselv-model"slide"transition-prev"scale"transition-next"scale"swipeableanimatedinfiniteautoplaynavigationpaddingarrowsheight"300px"class"bg-primary text…

PHP实现分离金额和其他内容便于统计计算

得到的结果可以粘贴到excel计算 <?php if($_GET["x"] "cha"){ $tips isset($_POST[tips]) ? $_POST[tips] : ; $pattern /(\d\.\d|\d)/; $result preg_replace($pattern, "\t\${1}\t", $tips); echo "<h2><strong>数…

ESRI中国培训资料(2013-2018年)

一、2013年培训资料 链接&#xff1a;https://pan.baidu.com/s/1BDQbOlpXGjEE3nLsQowJJg?pwd4j7v 提取码&#xff1a;4j7v 二、2014年培训资料 链接&#xff1a;https://pan.baidu.com/s/1DiDMgrIMz2D-XCAh8jCncA?pwdbfs9 提取码&#xff1a;bfs9 三、2015年培训资料 …

css实现梯形

<div class"trapezoid"></div> .trapezoid {width: 200px;height: 0;border-bottom: 100px solid red; /* 定义梯形的底边 */border-left: 50px solid transparent; /* 定义梯形的左边 */border-right: 50px solid transparent; /* 定义梯形的右边 */} …

Java 2:运算符、表达式和语句

2.1 运算符与表达式 Java提供了丰富的运算符&#xff0c;如算术运算符、关系运算符、逻辑运算符、位运算符等。Java语言中的绝大多数运算符和C语言相同&#xff0c;基本语句如条件分支语句&#xff0c;循环语句等&#xff0c;也和C语言类似。 2.1.1算术运算符与算术表达式 1…

Redis的常见面试题

目录 前言 Redis支持哪些数据类型 五种核心类型 Zset为什么用跳表不用红黑树 &#xff1f; Redis常见的应用场景&#xff1f; 如何检测Redis的连通性&#xff1f; 如何设置key的过期时间&#xff1f; Redis为什么是单线程模型&#xff1f; Redis里的IO多路复用是什…

[计网底层小探索]:实现并部署多线程并发Tcp服务器框架(基于生产者消费者模型的线程池结构)

文章目录 一.网络层与传输层协议sockaddr结构体继承体系(Linux体系)贯穿计算机系统的网络通信架构图示: 二.实现并部署多线程并发Tcp服务器框架线程池模块序列化反序列化工具模块通信信道建立模块服务器主体模块任务回调模块(根据具体应用场景可重构)Tips:DebugC代码过程中遇到…

MySQL学习笔记3: MySQL数据库基础

目录 前言目标数据库操作&#xff08;针对database 的操作&#xff09;1. 创建数据库 create database 数据库名;2. 查看数据库 show databases;3. 选中数据库 use 数据库名;4. 删除数据库 drop database 数据库名; mysql中支持的数据类型1. 数值类型: NUMERIC(M,D)2. 字符串类…

linux platform架构下I2C接口驱动开发

目录 概述 1 认识I2C协议 1.1 初识I2C 1.2 I2C物理层 1.3 I2C协议分析 1.3.1 Start、Stop、ACK 信号 1.3.2 I2C协议的操作流程 1.3.3 操作I2C注意的问题 2 linux platform驱动开发 2.1 更新设备树 2.1.1 添加驱动节点 2.1.2 编译.dts 2.1.3 更新板卡中的.dtb 2.2 …

良好的 API 安全策略的重要性

根据 Cloudflare 2024 年 API 安全与管理报告&#xff0c;到 2024 年&#xff0c;API 请求占全球动态互联网流量的 57%&#xff0c;这证实 API 是现代软件开发的重要组成部分。但随着多年来它们的采用不断增加&#xff0c;相关的安全挑战也随之增加。 在过去两年中&#xff0c…