代码随想录算法训练营DAY54|C++动态规划Part15|647.回文子串、516最长回文子序列、

本文主要是介绍代码随想录算法训练营DAY54|C++动态规划Part15|647.回文子串、516最长回文子序列、,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 647.回文子串
    • 思路
    • CPP代码
    • 双指针
  • 516最长回文子序列
    • 思路
    • CPP代码
  • 动态规划总结篇

647.回文子串

力扣题目链接

文章链接:647.回文子串

视频链接:动态规划,字符串性质决定了DP数组的定义 | LeetCode:647.回文子串

其实子串问题和子序列问题非常类似,也是存在最优子结构,那就意味着原问题的最优解可以由子问题的最优解推导出来。

其实回文的问题很容易联想到双指针。不过为了后续能够解决516.最长回文子序列问题,我们还是先使用动规解决一下该问题,为后面打基础。

思路

  • 确定dp数组以及下标含义

我们回文子串的问题并不能像解决子序列问题那样去设置dp数组。

如果我们定义,dp[i] 为 下标i结尾的字符串有 dp[i]个回文串的话,我们会发现很难找到递归关系,因为根本就无法跟相邻数组元素有什么关系。联想到使用双指针解决回文子串,我们的dp数组设置是不是也能参考设计呢

最好的解决方法设置:布尔类型的dp[i][j]:表示区间范围[i,j] (注意是左闭右闭)的子串是否是回文子串,如果是dp[i][j]为true,否则为false。

  • 确定递推公式

两种情况:s[i]与s[j]相等,s[i]与s[j]不相等,其中相等时又分为三种情况

如果不相等,那dp[i][j]=false

如果相等:

  1. 下标i 与 j相同,同一个字符例如a,当然是回文子串
  2. 下标i 与 j相差为1,例如aa,也是回文子串
  3. 下标:ij相差大于1的时候,例如cabac此时s[i]s[j]已经相同了,我们看ij区间是不是回文子串就看aba是不是回文就可以了,那么aba的区间就是 i+1j-1区间,这个区间是不是回文就看dp[i + 1][j - 1]是否为true

综上所述:

if (s[i] == s[j]) {if (j - i <= 1) { // 情况一 和 情况二result++;dp[i][j] = true;} else if (dp[i + 1][j - 1]) { // 情况三result++;dp[i][j] = true;}
}
  • 如何初始化

全是设置为false

  • 确定遍历顺序

二维的递推顺序最好就是根据递推公式画一个格子:

![在这里插入图片描述](

20210121171032473-20230310132134822

我们需要首先知道左下角的格子,才能推导出我们的dp,

所以一定要从下到上,从左到右遍历,这样保证dp[i + 1][j - 1]都是经过计算的

  • 推导dp数组

输入:“aaa”,dp[i][j]状态如下:

图片中有6个true,所以就有六个回文子串

CPP代码

//完整代码
class Solution {
public:int countSubstrings(string s) {vector<vector<bool>> dp(s.size(), vector<bool>(s.size(), false));int result = 0;for (int i = s.size() - 1; i >= 0; i--) {  // 注意遍历顺序for (int j = i; j < s.size(); j++) {if (s[i] == s[j]) {if (j - i <= 1) { // 情况一 和 情况二result++;dp[i][j] = true;} else if (dp[i + 1][j - 1]) { // 情况三result++;dp[i][j] = true;}}}}return result;}
};//简洁版代码
class Solution {
public:int countSubstrings(string s) {vector<vector<bool>> dp(s.size(), vector<bool>(s.size(), false));int result = 0;for (int i = s.size() - 1; i >= 0; i--) {for (int j = i; j < s.size(); j++) {if (s[i] == s[j] && (j - i <= 1 || dp[i + 1][j - 1])) {result++;dp[i][j] = true;}}}return result;}
};
  • 时间复杂度:O(n^2)
  • 空间复杂度:O(n^2)

双指针

回文必须还得是双指针

class Solution {
public:int countSubstrings(string s) {int count = 0;for (int i = 0; i < s.length(); ++i) {count += countPalindrome(s, i, i); // 以 s[i] 为中心的回文子串数量count += countPalindrome(s, i, i + 1); // 以 s[i] 和 s[i+1] 为中心的回文子串数量}return count;}private:int countPalindrome(const string& s, int left, int right) {int count = 0;while (left >= 0 && right < s.length() && s[left] == s[right]) {++count;--left;++right;}return count;}
};
  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)

516最长回文子序列

力扣题目链接

文章链接:516最长回文子序列

视频链接:动态规划再显神通,LeetCode:516.最长回文子序列

对于这个问题,使用双指针并不是最有效的方法。因为最长回文子序列不要求是连续的字符,而是可以跳过某些字符形成回文。本题中,回文子序列与回文子串是有区别的。

思路

  • 确定dp数组以及下标的含义

dp[i][j]:字符串s[i, j]范围内最长的回文子序列的长度为dp[i][j]

  • 确定递推公式

在判断回文子串的题目中,关键逻辑就是看s[i]与s[j]是否相同。

如果s[i]s[j]相同,那么dp[i][j] = dp[i + 1][j - 1] + 2;

具体如图所示:

如果s[i]与s[j]不相同,说明s[i]和s[j]的同时加入 并不能增加[i,j]区间回文子序列的长度,那么分别加入s[i]、s[j]看看哪一个可以组成最长的回文子序列

加入s[j]的回文子序列长度为dp[i + 1][j]

加入s[i]的回文子序列长度为dp[i][j - 1]

那么dp[i][j]一定是取最大的,即:dp[i][j] = max(dp[i + 1][j], dp[i][j - 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]);
}
  • dp数组如何初始化

从递推公式可以看出,递推公式计算不到i 和j相同时候的情况。

当i与j相同,那么dp[i][j]一定是等于1的,即:一个字符的回文子序列长度就是1。

其他都应该初始化成0,这样题对公式dp[i][j]=max(dp[i + 1][j], dp[i][j - 1]);才不会被初始值覆盖

vector<vector<int>> dp(s.size(), vector<int>(s.size(), 0));
for (int i = 0; i < s.size(); i++) dp[i][i] = 1;
  • 确定遍历顺序

从递归公式中,可以看出,dp[i][j] 依赖于 dp[i + 1][j - 1]dp[i + 1][j]dp[i][j - 1],如图:

所以当我们遍历i的时候是从上到下,然后j是从左到右

for (int i = s.size() - 1; i >= 0; i--) {for (int j = i + 1; j < s.size(); j++) {...}
}
  • 举例推导dp数组

输入s:“cbbd” 为例,dp数组状态如图:

20210127151521432

红色框即:dp[0][s.size() - 1]; 为最终结果。

CPP代码

class Solution {
public:int longestPalindromeSubseq(string s) {vector<vector<int>> dp(s.size(), vector<int>(s.size(), 0));for (int i = 0; i < s.size(); i++) dp[i][i] = 1;for (int i = s.size() - 1; i >= 0; i--) {for (int j = i + 1; j < s.size(); j++) {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][s.size() - 1];}
};

动态规划总结篇

二刷回来整总结!

这篇关于代码随想录算法训练营DAY54|C++动态规划Part15|647.回文子串、516最长回文子序列、的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


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

相关文章

Java List排序实例代码详解

《JavaList排序实例代码详解》:本文主要介绍JavaList排序的相关资料,Java排序方法包括自然排序、自定义排序、Lambda简化及多条件排序,实现灵活且代码简洁,文中通过代码介绍的... 目录一、自然排序二、自定义排序规则三、使用 Lambda 表达式简化 Comparator四、多条件排序五、

Java 压缩包解压实现代码

《Java压缩包解压实现代码》Java标准库(JavaSE)提供了对ZIP格式的原生支持,通过java.util.zip包中的类来实现压缩和解压功能,本文将重点介绍如何使用Java来解压ZIP或RA... 目录一、解压压缩包1.zip解压代码实现:2.rar解压代码实现:3.调用解压方法:二、注意事项三、总

Linux实现简易版Shell的代码详解

《Linux实现简易版Shell的代码详解》本篇文章,我们将一起踏上一段有趣的旅程,仿照CentOS–Bash的工作流程,实现一个功能虽然简单,但足以让你深刻理解Shell工作原理的迷你Sh... 目录一、程序流程分析二、代码实现1. 打印命令行提示符2. 获取用户输入的命令行3. 命令行解析4. 执行命令

SQL Server身份验证模式步骤和示例代码

《SQLServer身份验证模式步骤和示例代码》SQLServer是一个广泛使用的关系数据库管理系统,通常使用两种身份验证模式:Windows身份验证和SQLServer身份验证,本文将详细介绍身份... 目录身份验证方式的概念更改身份验证方式的步骤方法一:使用SQL Server Management S

uniapp小程序中实现无缝衔接滚动效果代码示例

《uniapp小程序中实现无缝衔接滚动效果代码示例》:本文主要介绍uniapp小程序中实现无缝衔接滚动效果的相关资料,该方法可以实现滚动内容中字的不同的颜色更改,并且可以根据需要进行艺术化更改和自... 组件滚动通知只能实现简单的滚动效果,不能实现滚动内容中的字进行不同颜色的更改,下面实现一个无缝衔接的滚动

利用Python实现可回滚方案的示例代码

《利用Python实现可回滚方案的示例代码》很多项目翻车不是因为不会做,而是走错了方向却没法回头,技术选型失败的风险我们都清楚,但真正能提前规划“回滚方案”的人不多,本文从实际项目出发,教你如何用Py... 目录描述题解答案(核心思路)题解代码分析第一步:抽象缓存接口第二步:实现两个版本第三步:根据 Fea

Java计算经纬度距离的示例代码

《Java计算经纬度距离的示例代码》在Java中计算两个经纬度之间的距离,可以使用多种方法(代码示例均返回米为单位),文中整理了常用的5种方法,感兴趣的小伙伴可以了解一下... 目录1. Haversine公式(中等精度,推荐通用场景)2. 球面余弦定理(简单但精度较低)3. Vincenty公式(高精度,

QT6中绘制UI的两种方法详解与示例代码

《QT6中绘制UI的两种方法详解与示例代码》Qt6提供了两种主要的UI绘制技术:​​QML(QtMeta-ObjectLanguage)​​和​​C++Widgets​​,这两种技术各有优势,适用于不... 目录一、QML 技术详解1.1 QML 简介1.2 QML 的核心概念1.3 QML 示例:简单按钮

Java进行日期解析与格式化的实现代码

《Java进行日期解析与格式化的实现代码》使用Java搭配ApacheCommonsLang3和Natty库,可以实现灵活高效的日期解析与格式化,本文将通过相关示例为大家讲讲具体的实践操作,需要的可以... 目录一、背景二、依赖介绍1. Apache Commons Lang32. Natty三、核心实现代

使用雪花算法产生id导致前端精度缺失问题解决方案

《使用雪花算法产生id导致前端精度缺失问题解决方案》雪花算法由Twitter提出,设计目的是生成唯一的、递增的ID,下面:本文主要介绍使用雪花算法产生id导致前端精度缺失问题的解决方案,文中通过代... 目录一、问题根源二、解决方案1. 全局配置Jackson序列化规则2. 实体类必须使用Long封装类3.