动态规划课堂6-----回文串问题

目录

引言:

例题1:回文子串

例题2:回文串分割IV

例题3:分割回文串II

例题4:最长回文子序列

例题5:让字符串成为回文串的最小插入次数


引言:

回文字符串 是正着读和倒过来读一样的字符串。

动态规划的回文串问题一般是把子串是否是回文串的信息保持在dp表里面,所以更多的时候回文串的dp表只是起到一个辅助的作用,有一些题要利用回文串dp表再做一次动态规划,其实很多困难题某一些步骤都是可以动态规划来化简的。😎😎😎

例题1:回文子串

链接:回文子串

题目简介:

给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。

回文字符串 是正着读和倒过来读一样的字符串。

子字符串 是字符串中的由连续字符组成的一个序列。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

解法(动态规划):

当然这题的最优解不是动态规划而是中心拓展算法,但是我们这里主要叙述动态规划。

这一题其实就是一个把回文子串信息保存在dp表里面的模板题😎

对于本题我们可以先预处理⼀下,将所有子串是否回⽂的信息统计在dp 表⾥⾯,然后直接在表里面统计true 的个数即可。

 1. 状态表示:

dp[i][j] 表示: s 字符串[i, j] 的子串,是否是回文串。

这个二维的dp表其实只需用到上三角的地方,因为j是大于等于i的。

 2.状态转移方程:

对于回文串,我们⼀般分析⼀个区间两头的元素:例如下图利用最外层和内层的递推关系完成动态规划。

(1)当s[i] != s[j] 的时候:不可能是回文串, dp[i][j] = 0 ; 

(2)当s[i] == s[j] 的时候:根据长度分三种情况讨论:

1.⻓度为1 ,也就是i == j ,此时⼀定是回文串, dp[i][j] = true。

2.⻓度为2 ,也就是i + 1 == j :此时也⼀定是回⽂串, dp[i][j] = true 。

3.⻓度⼤于2 ,此时要去看看[i + 1, j - 1] 区间的子串是否回文: dp[i][j] = dp[i + 1][j - 1] 。

 3.初始化:

无需初始化,因为我们填表的范围如下图,会越界的节点在对角线,但是我们上面的状态转移方程已经把i == j的情况特判了,所以不会越界。

 4.填表顺序:

从下往上

 5.返回值:

返回dp 表中true 的个数。

代码如下:

dp[i][j] = i + 1 < j ? dp[i + 1][j - 1] : true;解释如下:因为i == j 和i + 1 == j 的情况下dp[i][j]都为true,长度大于2对应i + 1 < j 的情况。

class Solution {public int countSubstrings(String ss) {//1.创建 dp 表//2.初始化//3.填表//4.返回值int n = ss.length();char[] s = ss.toCharArray();boolean[][] dp = new boolean[n][n];int count = 0;for(int i = n - 1;i >= 0;i--){for(int j = i;j < n;j++){if(s[i] == s[j]){dp[i][j] = i + 1 < j ? dp[i + 1][j - 1] : true;}if(dp[i][j] == true){count++;}}}return count;}
}

时间复杂度:O(n^2)

空间复杂度:O(n^2)

下面我给出一题大家练练手,解法过程和例题1差不多就是最后创建完回文dp表最后的返回值不一样。最长回文子串

例题2:回文串分割IV

链接:回文串分割IV

题目简介:

给你一个字符串 s ,如果可以将它分割成三个 非空 回文子字符串,那么返回 true ,否则返回 false 。

当一个字符串正着读和反着读是一模一样的,就称其为 回文字符串 。

关于预处理所有子串是否回文,已经在上⼀道题目里面讲过,这里就不再赘述啦~

先把回文dp表填好,接下来枚举三个子串除字符串端点外的起止点,查询这三段非空子串是否是回文串。 枚举i和j这两条分界线即可,注意区间的闭合问题和J必须要大于等于i。

代码如下:

1. 利用 dp 处理⼀下所有的子串是否回文。

2. 枚举第二个字符串所有的起始位置和终止位置。

写代码希望大家按照1.创建 dp 表2,初始化,3.填表,4.返回值的顺序来进行书写代码,这样不会乱。

class Solution {public boolean checkPartitioning(String s) {//1.创建 dp 表//2.初始化//3.填表//4.返回值int n = s.length();boolean[][] dp = new boolean[n][n];for(int i = n - 1;i >= 0;i--){for(int j = i;j < n;j++){if(s.charAt(i) == s.charAt(j)){dp[i][j] = i + 1 < j ? dp[i + 1][j - 1] : true;}}}boolean ans = false;for(int i = 1;i < n;i++){for(int j = i;j < n - 1;j++){if(dp[0][i - 1] && dp[i][j] && dp[j + 1][n - 1]){ans = true;return ans;}}}return false;}
}

时间复杂度:O(n^2)

空间复杂度:O(n^2)

例题3:分割回文串II

链接:分割回文串II

题目简介:

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是回文串。

返回符合要求的 最少分割次数 。

  解法(动态规划):

 1. 状态表示:

dp[i] 表示: s中[0, i] 区间上的字符串,最少分割的次数。

 2.状态转移方程:

状态转移方程⼀般都是根据最后⼀个位置的信息来分析:设0 <= j <= i ,那么我们可以根据j ~ i位置上的子串是否是回文串分成下面两类:

(1)当[j ,i] 位置上的子串能够构成⼀个回文串,那么dp[i] 就等于[0, j - 1] 区间上最少回文串的个数+1,即dp[i] = dp[j - 1] + 1 。

(2)当[j ,i] 位置上的⼦串不能构成⼀个回文串,此时j 位置就不⽤考虑。

由于我们要的是最小值,因此应该循环遍历⼀遍j的取值,拿到里面的最小值即可。

优化:我们在状态转移⽅程里面分析到,要能够快速判读字符串里面的⼦串是否回文。因此,我们 可以先处理⼀个dp 表,里面保存所有⼦串是否回文的信息。

 3.初始化:

为了防止求min操作时,0干扰结果。我们先把表里面的值初始化为无穷大。

 4.填表顺序:

从左往右

 5.返回值:

应该返回dp[n - 1]。

代码如下:

其中algorithm就是回文串信息的dp表。

class Solution {public int minCut(String s) {//1.创建 dp 表//2.初始化//3.填表//4.返回值int n = s.length();int[] dp = new int[n];boolean[][] algorithm = new boolean[n][n];for(int i = n - 1;i >= 0;i--){for(int j = i;j < n;j++){if(s.charAt(i) != s.charAt(j)){algorithm[i][j] = false;}else{algorithm[i][j] = i + 1 < j ? algorithm[i + 1][j - 1] : true;}}}for(int i = 0;i < n;i++){dp[i] = 0x3f3f3f3f;}for(int i = 0;i < n;i++){for(int j = 0;j <= i;j++){if(algorithm[0][i]){dp[i] = 0;break;}else{if(algorithm[j][i]){dp[i] = Math.min(dp[i],dp[j - 1] + 1);}}}}return dp[n - 1];}
}

时间复杂度:O(n^2)

空间复杂度:O(n^2)

例题4:最长回文子序列

链接:最长回文子序列

题目简介:

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

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

 解法(动态规划):

 1. 状态表示:

回文串问题一般取左右区间,以左右节点来分析。

dp[i][j] 表示:s字符串[i, j] 区间内的所有的子序列中,最长的回文子序列的长度。

 2.状态转移方程:

关于回文子序列和回文子串的分析⽅式,⼀般都是⽐较固定的,都是选择这段区域的左右端点的字符情况来分析。因为如果⼀个序列是回文串的话,去掉⾸尾两个元素之后依旧是回⽂串,⾸尾加上两个相同的元素之后也依旧是回文串。因为,根据首尾元素的不同,可以分为下面两种情况:

(1)当首尾两个元素相同的时候,也就是s[i] == s[j] :那么[i, j] 区间上的最长回文子序列,应该是[i + 1, j - 1] 区间内的那个最长回文子序列首尾填上s[i] 和s[j] ,此时dp[i][j] = dp[i + 1][j - 1] + 2 。

(2)当首尾两个元素不相同的时候,也就是s[i] != s[j] :此时这两个元素就不能同时添加在⼀个回⽂串的左右,那么我们就应该让s[i] 单独加在⼀个序列的左边,或者让s[j] 单独放在⼀个序列的右边,看看这两种情况下的最大值:

1.单独加⼊s[i] 后的区间在[i, j - 1] ,此时最长的回⽂序列的长度就是dp[i] [j - 1] 。

2.单独加⼊s[j] 后的区间在[i + 1, j] ,此时最长的回⽂序列的长度就是dp[i + 1][j] 。

取两者的最大值,于是dp[i][j] = max(dp[i][j - 1], dp[i + 1][j])。

综上所述,状态转移方程为:

(1)当s[i] == s[j] 时: dp[i][j] = dp[i + 1][j - 1] + 2 .

(2)当s[i] != s[j] 时: dp[i][j] = max(dp[i][j - 1], dp[i + 1][j]) .

 3.初始化:

根据状态转移⽅程dp[i][j] = dp[i + 1][j - 1] + 2 ,我们状态表⽰的时候,选取的是⼀段区间,因此需要要求左端点的值要⼩于等于右端点的值,因此会有两种边界情况:

(1)当i == j 的时候, i + 1 就会大于j - 1 ,此时区间内只有⼀个字符。这个⽐较好分析, dp[i][j] 表示⼀个字符的最长回文序列,⼀个字符能够自己组成回文串,因此此时dp[i][j] = 1。

(2)当i + 1 == j的时候, i + 1 也会大于j - 1 ,此时区间内有两个字符。这样也好分析,当这两个字符相同的时候, dp[i][j] = 2 ;不相同的时候, d[i][j] = 0.

对于第⼀种边界情况,我们在填表的时候,就可以同步处理。

对于第⼆种边界情况, dp[i + 1][j - 1] 的值为0 ,不会影响最终的结果,因此可以不用考虑。

 4.填表顺序:

从下往上填写每⼀行。

每⼀行从左往右。

 5.返回值: 

根据状态表示,我们需要返回[0, n -1] 区域上的最长回文序列的长度,因此需要返回dp[0][n - 1] 。

代码如下:

class Solution {public int longestPalindromeSubseq(String s) {//.创建 dp 表//2.初始化//3.填表//4.返回值int n = s.length();int[][] dp = new int[n][n];for(int i = n - 1;i >= 0;i--){//j必须要大于等于ifor(int j = i;j < n;j++){if(s.charAt(i) == s.charAt(j)){if(i == j){dp[i][j] = 1;}else if(i + 1 == j){dp[i][j] = 2;}else{dp[i][j] = dp[i + 1][j - 1] + 2;}}else{dp[i][j] = Math.max(dp[i][j - 1],dp[i + 1][j]);}}}return dp[0][n - 1];}
}

时间复杂度:O(n^2)

空间复杂度:O(n^2)

例题5:让字符串成为回文串的最小插入次数

链接:让字符串成为回文串的最小插入次数

题目简介:

给你一个字符串 s ,每一次操作你都可以在字符串的任意位置插入任意字符。

请你返回让 s 成为回文串的 最少操作次数 。

回文串是正读和反读都相同的字符串。

  解法(动态规划):

 1. 状态表示:

状态表示: dp[i][j]表示字符串[i, j] 区域成为回文子串的最少插入次数。

 2.状态转移方程:

根据首尾元素的不同,可 以分为下⾯两种情况:

(1)当首尾两个元素相同的时候,也就是s[i] == s[j] :

1.那么 [i, j] 区间内成为回文子串的最少插⼊次数,取决于[i + 1, j - 1] 区间内成为回文子串的最少插入次数。

2.若 i == j 或i == j - 1( [i + 1, j - 1] 不构成合法区间),此时只有1 ~2个相同的字符, [i, j] 区间⼀定是回文子串,成为回文子串的最少插入次数是0.

(2)当首尾两个元素不相同的时候,也就是s[i] != s[j] :

1.此时可以在区间最右边补上⼀个 s[i] ,需要的最少插入次数是 [i + 1, j] 成为回文子串的最少插⼊次数+本次插入,即 dp[i][j] = dp[i + 1][j] + 1 。

2.此时可以在区间最左边补上⼀个 s[j] ,需要的最少插入次数是 [i, j + 1] 成为回文子串的最少插⼊次数+本次插⼊,即 dp[i][j] = dp[i][j + 1] + 1 。

综上所述,状态转移方程为:

(1)当s[i] == s[j]时: dp[i][j] = i >= j - 1 ? 1 : dp[i + 1][j - 1]。

(2)s[i] != s[j]时: dp[i][j] = min(dp[i + 1][j], dp[i][j - 1]) + 1 。

 3.初始化:

没有不能递推表示的值,无需初始化。

 4.填表顺序:

从下往上填写每⼀行

从下往上填写每⼀行

 5.返回值:

返回dp[0][n - 1]。

代码如下:

class Solution {public int minInsertions(String s) {//1.创建 dp 表//2.初始化//3.填表//4.返回值int n = s.length();int[][] dp = new int[n][n];for(int i = n - 1;i >= 0;i--){for(int j = i;j < n;j++){if(s.charAt(i) == s.charAt(j)){dp[i][j] = i + 1 < j ? dp[i + 1][j - 1] : 0;}else{dp[i][j] = Math.min(dp[i + 1][j] + 1,dp[i][j - 1] + 1);}}}return dp[0][n - 1];}
}

时间复杂度:O(n^2)

空间复杂度:O(n^2)

结语:

其实写博客不仅仅是为了教大家,同时这也有利于我巩固知识点,和做一个学习的总结,由于作者水平有限,对文章有任何问题还请指出,非常感谢。如果大家有所收获的话还请不要吝啬你们的点赞收藏和关注,这可以激励我写出更加优秀的文章。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【周赛】第385场周赛

&#x1f525;博客主页&#xff1a; A_SHOWY&#x1f3a5;系列专栏&#xff1a;力扣刷题总结录 数据结构 云计算 数字图像处理 力扣每日一题_ 【1】100212.统计前后缀下标对 100212. 统计前后缀下标对 Ihttps://leetcode.cn/problems/count-prefix-and-suffix-pairs-i/ 熟…

4.1_4 文件的物理结构

文章目录 4.1_4 文件的物理结构&#xff08;一&#xff09;文件块、磁盘块&#xff08;二&#xff09;文件分配方式——连续分配&#xff08;三&#xff09;文件分配方式——链接分配&#xff08;1&#xff09;链接分配——隐式链接&#xff08;2&#xff09;链接分配——显式链…

OpenStack之存储cinder

一、 存储 1、DAS 1)存储更多的依赖服务器主机操作系统进行数据的IO读写和存储维护管理&#xff0c;数据备份和恢复要求占用服务器主机资源&#xff08;包括CPU、系统IO等&#xff09; 2)直连式存储与服务器主机之间的连接通道通常采用SCSI连接&#xff0c;随着服务器CPU的处…

Type-C接口介绍

1、USB介绍 &#xff08;1&#xff09;标准USB A型连接器&#xff08;左&#xff09;及B型连接器&#xff08;右&#xff09; 引脚1 VCC&#xff08;5V&#xff09; 引脚2 Data- 引脚3 Data 引脚4 接地 &#xff08;2&#xff09;Micro USB 引脚定义及OTG (USB-HOST) …

k8s CKA upgrade - Kubeadm 版本升级实测

升级版本最好是逐步去升级&#xff0c;不要跨越多个大版本&#xff0c;可能会出错 大体流程&#xff1a; 1.先确定升级版本 2.升级kubeadm 3.驱逐节点 4.升级kubelet和kubectl 5.重启kubelet服务 6.恢复节点&#xff0c;使其上线 1.查看现版本&#xff1a;升级版本 kubectl ge…

华为配置Hotspot2.0无线网络示例

配置Hotspot2.0无线网络示例 组网图形 图1 配置Hotspot2.0无线网络组网图 组网需求配置思路配置注意事项操作步骤配置文件 组网需求 某网络服务商在原有移动网络业务的基础上&#xff0c;新增部署WLAN网络接入业务&#xff0c;为用户提供更好的网络体验。但传统的WLAN网络业…

【每日一题】2024年3月汇编(上)

3.1【2369】检查数组是否存在有效划分 2369. 检查数组是否存在有效划分https://leetcode.cn/problems/check-if-there-is-a-valid-partition-for-the-array/ 1.这样的判断可以用动态规划来解决&#xff0c;用一个长度为(n1) 的数组来记录 是否存在有效划分&#xff0c;dp[i]…

Bean的作用域、Bean的自动装配、注解自动装配 (Spring学习笔记五)

1、Bean 的作用域 官网上显示有六种 1、Bean的作用域默认的是singleton&#xff08;单例模式的实现&#xff09; 也可以显示的设置&#xff08;单例模式的实现&#xff09; <!--用scope可以设置Bean的作用域--><bean id"user2" class"com.li.pojo.Us…

NCV8705MTADJTCG稳压器芯片中文资料规格书PDF数据手册引脚图图片价格功能

产品概述&#xff1a; NCV8705 是一款低噪音、低功耗和低泄漏线性电压稳压器。该器件具有卓越的噪音和 PSRR 规格&#xff0c;适用于使用视频接收器、成像传感器、音频处理器或需要外部洁净电源的任何部件的产品。NCV8705 使用创新的自适应接地电流电路 可确保轻负载调节下的超…

http请求方法15种,附图可以下载保存备查。

一、http请求组成和流程 HTTP请求是客户端&#xff08;如浏览器&#xff09;向服务器发送的请求&#xff0c;以获取特定资源或执行特定操作。HTTP请求由以下几个部分组成&#xff1a; 请求行&#xff1a;包含请求方法、请求的URL和HTTP协议版本。常见的请求方法有GET、POST、P…

数据结构的概念大合集01(含数据结构的基本定义,算法及其描述)

概念大合集01 1、数据结构基础的定义2、数据结构2.1 数据元素之间关系的集合2.2数据结构的三要素2.2.1数据的逻辑结构2.2.2数据的存储&#xff08;物理&#xff09;结构2.2.3数据的运算 3、数据类型4、抽象数据类型类型&#xff08;ADT&#xff09;5、算法及其描述5.1算法的5个…

Qt文件以及文件夹相关类(QDir、QFile、QFileInfo)的使用

关于Qt相关文件读写操作以及文件夹的一些知识&#xff0c;之前也写过一些博客&#xff1a; Qt关于路径的处理&#xff08;绝对路径、相对路径、路径拼接、工作目录、运行目录&#xff09;_qt 相对路径-CSDN博客 C/Qt 读写文件_qt c 读取文本文件-CSDN博客 C/Qt读写ini文件_…

Docker学习之数据管理(超详解析)

Docker存储资源类型&#xff1a; 用户在使用 Docker 的过程中&#xff0c;势必需要查看容器内应用产生的数据&#xff0c;或者需要将容器内数据进行备份&#xff0c;甚至多个容器之间进行数据共享&#xff0c;这必然会涉及到容器的数据管理&#xff1a; &#xff08;1&#xff…

(含代码)利用NVIDIA Triton加速Stable Diffusion XL推理速度

在 NVIDIA AI 推理平台上使用 Stable Diffusion XL 生成令人惊叹的图像 扩散模型正在改变跨行业的创意工作流程。 这些模型通过去噪扩散技术迭代地将随机噪声塑造成人工智能生成的艺术&#xff0c;从而基于简单的文本或图像输入生成令人惊叹的图像。 这可以应用于许多企业用例&…

【剑指offer--C/C++】JZ25 合并两个排序的链表

题目 思路 这个题目大逻辑比较简单&#xff0c;就是一个比较和穿插&#xff0c;但细节上要考虑清楚&#xff0c;可以画个图模拟一下。我这里是设置将两个链表拆开组成一个新的链表&#xff0c;这样不需要占用新的空间。两个指针对应节点的值进行比较&#xff0c;那个节点值较小…

【Stable Diffusion】入门-03:图生图基本步骤+参数解读

目录 1 图生图原理2 基本步骤2.1 导入图片2.2 书写提示词2.3 参数调整 3 随机种子的含义4 拓展应用 1 图生图原理 当提示词不足以表达你的想法&#xff0c;或者你希望以一个更为简单清晰的方式传递一些要求的时候&#xff0c;可以给AI输入一张图片&#xff0c;此时图片和文字是…

数据可视化学习:Matplotlib概述

一、图表的常用设置 1.基本绘图主要函数 (1).matplotlib.pyplot.plot(x,y,format_string,**kwargs) 2.参数说明 (1).x:x轴数据 (2).y:y轴数据 (3).format_string:控制曲线格式的字符串&#xff0c;包括颜色、线条样式和标记样式 (4)**kwargs:键值参数&#xff0c;相当于…

基础知识学习 -- qnx 系统

QNX是一个基于优先级抢占的系统。 这也导致其基本调度算法相对比较简单。因为不需要像别的通用操作系统考虑一些复杂的“公平性”&#xff0c;只需要保证“优先级最高的线程最优先得到 CPU”就可以了。 基本调度算法 调度算法&#xff0c;是基于优先级的。QNX的线程优先级&a…

VS调试快捷键

VS调试快捷键 4. VS调试快捷键4.1 环境准备4.2 调试快捷键调试最常使⽤的⼏个快捷键&#xff1a; 4. VS调试快捷键 那程序员怎么调试代码呢&#xff1f; 4.1 环境准备 首先是环境的准备&#xff0c;需要⼀个支持调试的开发环境&#xff0c;我们上课使用VS&#xff0c;应该把…

Magical Combat VFX

这个包包含30个可供游戏使用的VFX,有各种口味,为您的游戏增添趣味! 所有VFX都经过了很好的优化,可以在所有平台上使用。 这个包特别有一堆闪电魔法,有两种主要的变体,一种是深色的,另一种是浅色的。但它也提供了一系列其他视觉效果,如神圣咒语、音乐主题等等! 我们提供…