代码随想录训练营第56天|LeetCode 647. 回文子串、516.最长回文子序列

news2024/11/18 12:27:07

参考

代码随想录

题目一:LeetCode 647. 回文子串

如果用暴力求解,两层for循环加一层判断,两个遍历指针i和j构成一个区间,每次判断这个区间内的字符串是否为回文串,这样的求法时间复杂度为O(n^3)。这里使用动态规划可以将判断i和j区间的字符串是否为回文串的时间复杂度降为O(1).
在这里插入图片描述
如上图所示,如果使用暴力解法,那么需要遍历[i,j]区间中的每个字符,但使用动态规划就不需要遍历这个区间内的所有字符了,只需要判断s[i]和s[j]是否相等,前提是要先记录[i+1,j-1]区间内的情况,因此需要占用额外的空间,也就是用空间换取时间的方法。因为判断[i,j]区间的字符串是否为回文串就需要先知道[i+1,j-1]区间内的字符串是否为回文串,因此整个区间要先从最右边开始“扩张”,即i从最右边开始向左移动,j指针从i开始向右移动。

  1. 确定dp数组下标及其含义
    dp[i][j]:如果[i,j]区间内的字符串为回文串,则dp[i][j]为true,否则为false。

  2. 确定递推公式

  • 如果s[i] != s[j],则[i,j]内的字符串必定不是回文串,即dp[i][j] = false.
  • 如果s[i] == s[j],则需要分为三种情况:
    (1)i == j,即区间内只有一个字符,因此dp[i][j] = true
    (2)i + 1 = j,即区间内只有两个字符,因此是回文串,dp[i][j] = true
    (3)i + 1 < j,即区间内多于两个字符,此时是否为回文串要看dp[i+1][j-1],如果dp[i+1][j-1]为true,则是回文串,否则不是

综上所述,递推公式的代码实现为:

if(s[i] != s[j])
   dp[i][j] = false;
else{
    if(i == j || i + 1 == j || ((i + 1 < j) && dp[i+1][j-1] == true)){	//三种为true的情况
        dp[i][j] = true;
        result ++;
    }
    else
        dp[i][j] = false;
}
  1. 确定遍历顺序
    从之前的分析就提到,[i,j]区间内字符串的判断可能要依赖[i+1,j-1]区间的判断结果,因此整个区间必定是要从最右边开始向左移动,因此i从大到小,j从i开始递增。

  2. 初始化dp数组
    根据递推公式,似乎需要初始化dp数组,但其实可以不初始化,因为只有[i,j]区间内的元素多于两个时候才需要用到dp[i+1][j-1],当[i,j]区间只有一个或两个元素的时候直接就能得到结论,因此可以看作初始化过程已经包含在遍历过程中了,因此不需要额外初始化。

  3. 举例推导dp数组
    在这里插入图片描述
    可以看出,因为i <= j,因此dp数组只用到了对角线及右上半部分,图中有6个true,因此有6个回文子串。

完整的代码实现如下:

class Solution {
public:
    int countSubstrings(string s) {
        int result = 0;
        vector<vector<bool>> dp(s.size(), vector<bool>(s.size()));
        for(int i = s.size(); i >= 0; i--){
            for(int j = i; j < s.size(); j++){
                if(s[i] != s[j])
                    dp[i][j] = false;
                else{
                    if(i == j || i + 1 == j || ((i + 1 < j) && dp[i+1][j-1] == true)){	//三种为true的情况
                        dp[i][j] = true;
                        result ++;
                    }
                    else
                        dp[i][j] = false;
                }
            }
        }
        return result;
    }
};

题目二:LeetCode 516.最长回文子序列

  1. 确定dp数组下标及其含义
    dp[i][j]:字符串s中[i,j]区间构成的字符串中的最长回文子串的长度为dp[i][j]。

  2. 确定递推公式

  • 如果s[i] == s[j],则dp[i][j] = dp[i+1][j-1] + 2,其中的加2就是新加入的两个相等的字符
  • 如果s[i] != s[j],说明同时加入s[i]和s[j]并不能增加回文子序列的长度,那么如果加入s[i],则回文串的最大长度为dp[i][j-1],如果加入s[j],那么dp[i+1][j],最终要取最大,即dp[i][j] = max(dp[i][j-1],dp[i+1][j]。为什么do[i][j] = dp[i-1][j-1]的情况?因为dp[i][j] <= dp[i][j-1]且dp[i][j] <= dp[i+1][j],因此dp[i][j-1]和dp[i+1][j]已经涵盖了dp[i-1][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]);
  1. 初始化dp数组
    初始化要结合下图理解。因为要满足i <= j,所以似乎do数组只有下图中的阴影部分有用。
    在这里插入图片描述
    根据递推关系,dp[i][j]会涉及到下图中的几个区域:
    在这里插入图片描述
    当i == j或者i + 1 = j,即区间中只有一个或者两个字符,此时dp[i][j]所依赖的元素就会在对角线的左下半部分,如下图所示:
    在这里插入图片描述
    因为当i == j时,dp[i][j]确定等于1,因此在初始化时就将i == j的情况赋值,在遍历时就不考虑了。
    当i + 1 == j时,区间内有两个字符,如果两个字符相等,则dp[i][j] = 2,否则dp[i][j] = 1。套用递推公式,如果s[i] == s[j],则dp[i][j] = dp[i+1][j-1] + 2 = 2,推出dp[i+1][j-1] = 0,根据 i + 1 == j,dp[i+1][j-1]是下图中的阴影部分,这部分应该初始化为0。
    在这里插入图片描述
    如果s[i] != s[j],则dp[i][j] = max(dp[i+1][j],dp[i][j-1]) = 1,同样因为 i + 1 == j,所以此时的dp[i+1][j]和dp[i][j-1]是dp数组的对角线,所以对角线应该初始化为1,和只有一个元素时的情况一样,没有矛盾,所以可以一起处理。

综上所述,dp数组的初始化如下图所示(其余不用初始化):
在这里插入图片描述
为了方便,其余都初始化为0。

  1. 确定遍历顺序
    根据递推公式的依赖关系(如下图所示),i要从大到小,j要从小到大。
    在这里插入图片描述
  2. 举例推导dp数组
    在这里插入图片描述
    完整的代码实现如下:
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].back();
    }
};

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

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

相关文章

Jetpack Compose中的手势操作

点击事件 监听点击事件非常简单&#xff0c;使用 clickable 和 combinedClickable 修饰符即可满足需求&#xff1a; OptIn(ExperimentalFoundationApi::class) Composable fun ClickableExample() {Column{Box(Modifier.clickable { println("clickable") }.size(3…

异常检测原理及其在计算机视觉中的应用

点击上方“小白学视觉”&#xff0c;选择加"星标"或“置顶”重磅干货&#xff0c;第一时间送达这篇文章涵盖了三件事&#xff0c;首先什么是视觉角度的异常检测&#xff1f;用于异常检测的技术有哪些&#xff1f;它在哪里使用&#xff1f;异常检测是什么&#xff1f;…

微服务架构下的配置治理模式

微服务被滥用是不争的事实。被滥用的同时&#xff0c;很少人留意到它所带来的配置治理的问题。本文我们介绍两种常见的治理模式。基于common的配置治理模式当微服务数量多时&#xff0c;开发人员倾向于创建这样的配置文件&#xff1a;common-redis.jsoncommon-mysql.jsoncommon…

〖产品思维训练白宝书 - 产品思维认知篇③〗- 产品思维 VS 技术思维

大家好&#xff0c;我是 哈士奇 &#xff0c;一位工作了十年的"技术混子"&#xff0c; 致力于为开发者赋能的UP主, 目前正在运营着 TFS_CLUB社区。 &#x1f4ac; 人生格言&#xff1a;优于别人,并不高贵,真正的高贵应该是优于过去的自己。&#x1f4ac; &#x1f4e…

【Numpy基础知识】Broadcasting广播

Numpy广播 来源&#xff1a;Numpy官网&#xff1a;https://numpy.org/doc/stable/user/basics.html 广播描述了 NumPy 在算术运算期间如何处理具有不同形状的数组。根据某些约束&#xff0c;较小的数组将“广播”到较大的阵列&#xff0c;以便它们具有兼容的形状。 导包 impo…

【Python机器学习】条件随机场模型CRF及在中文分词中实战(附源码和数据集)

需要源码请点赞关注收藏后评论区留言私信~~~ 基本思想 假如有另一个标注序列&#xff08;代词 动词 名词 动词 动词&#xff09;&#xff0c;如何来评价哪个序列更合理呢&#xff1f; 条件随机场的做法是给两个序列“打分”&#xff0c;得分高的序列被认为是更合理的。既然要…

移动设备软件开发-Shape详解

Spape详解 1.自定义背景shape 1.1gradient 1.简介 定义渐变色&#xff0c;可以定义两色渐变和三色渐变&#xff0c;及渐变样式&#xff0c;它的属性有下面几个2.属性 angle&#xff0c;只对线性渐变是有效的放射性渐变必须指定放射性的半径&#xff0c;gradientRadiouscentetX和…

STM32的ST-link调试下载,各种调试接口硬件介绍

调试原理 STM32F-10X使用M3内核&#xff0c;该内核支持复杂的同i傲视操作&#xff0c;硬件调试模块允许在取指令&#xff08;指令单步运行&#xff09;或访问数据&#xff08;数据断电时&#xff09;使得内核停止。在内核停止时&#xff0c;内核状态都可被查询&#xff0c;完成…

JS基于编码方式实现加密解密文本

JS基于编码方式实现加密解密文本 严格来讲这是一种简单的编码方式&#xff1a;加密&#xff0c;将明文【注】转成编码。解密则是编码转码为明文本。 【注&#xff1a;明文是指没有加密的文字(或者字符串)&#xff0c;一般人都能看懂。】 下面源码用到 这个fromCharCode() 方…

年底了,感谢大家2022年的支持,虚竹哥送10本JAVA好书

❤️作者主页&#xff1a;小虚竹 ❤️作者简介&#xff1a;大家好,我是小虚竹。Java领域优质创作者&#x1f3c6;&#xff0c;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;掘金年度人气作者&#x1f3c6;&#xff0c;阿里云专家博主&#x1f3…

碧兴物联IPO过会:拟募资4亿 预计年净利降幅超10%

雷递网 雷建平 12月21日碧兴物联科技&#xff08;深圳&#xff09;股份有限公司&#xff08;简称&#xff1a;“碧兴物联”&#xff09;日前IPO过会&#xff0c;准备在科创板上市。碧兴物联计划募资4.13亿元。其中&#xff0c;1.92亿元用于智慧生态环境大数据服务项目&#xff…

【MySQL】JDBC编程重点知识汇总

文章目录1. JDBC (API):2. JDBC代码编写:2.1 创建数据源对象:2.2 建立连接:2.3 构造SQL语句:2.4 执行SQL:2.5 释放资源:1. JDBC (API): 前面学过很多的SQL, 实际开发中大多数的SQL都不是手敲的, 都是通过程序来执行的. 各种的数据库都会提供API方便编程语言来控制; API (Appli…

【JavaEE】网络初识

初识网络协议 OSI七层和TCP/IP五层&#xff08;四层&#xff09; 应用层 应用程序 代码实现 传输层 端到端传输 &#xff08;如玩家对玩家&#xff09; 操作系统内核实现 网络层 点到点传输 操作系统内核实现 数据链路层 相邻节点之间的传输 &#xff08;如集散点…

ChatGPT能接入微信了

前两天还看到不少人讨论&#xff0c;要是ChatGPT接入微信是啥感觉&#xff1f; 这不&#xff0c;想你所想&#xff0c;项目已经来了~ 来看效果&#xff0c;ChatGPT就出现在普通的微信对话框里&#xff0c;有问必答&#xff1a; 甚至还能拉入群聊&#xff0c;大家共用&#xf…

Adobe Premiere Pro 2020 系统兼容性报告:不支持的视频驱动程序

Adobe Premiere Pro 2020 系统兼容性报告&#xff1a;不支持的视频驱动程序 1. 问题 打开Adobe Premiere Pro 2020&#xff0c;看见系统兼容性报告&#xff1a;不支持的视频驱动程序。如下图&#xff1a; 点击修复&#xff0c;进入安装 Intel 图形驱动程序教程页面&#xff0…

DQL查询数据

文章目录DQL指定查询字段where条件子句联表查询分页和排序子查询DQL &#xff08;Data Query Language&#xff1a;数据查询语言&#xff09; 所有的查询操作都要用到它 select简单的查询&#xff0c;复杂的查询都要用到它数据库最核心的语言&#xff0c;最重要的语言使用频率…

python写个网页,使用flask显示时间登陆注册

用python写个网页。显示当前时间 可以使用 Python 的 datetime 模块来获取当前时间&#xff0c;然后使用 Python 的 Flask 框架来创建网页。 首先&#xff0c;需要安装 Flask&#xff1a; pip install flask 然后&#xff0c;可以使用以下代码创建一个 Flask 应用程序&#…

【Numpy基础知识】字节交换

字节交换 来源&#xff1a;Numpy官网&#xff1a;https://numpy.org/doc/stable/user/basics.html 文章目录字节交换导包【1】字节排序和ndarrays 简介【2】更改字节顺序导包 import numpy as np【1】字节排序和ndarrays 简介 ndarray 是一个对象&#xff0c;它为内存中的数据…

2023年,我的儿子刚从美国名校毕业,就失业了...

前不久&#xff0c;朋友圈里一篇名为《2023年&#xff0c;我的儿子刚从美国名校毕业&#xff0c;就失业了…》的文章火爆全网。 故事里的男孩出生于一个中产阶级家庭&#xff0c;从每年12万的幼儿园开始一路接受了优质教育&#xff0c;最终不负众望从美国前50名校的商学院毕业…

Ubuntu20.04LTS环境docker+cephadm方式部署Ceph 17.2.5

Ubuntu20.04LTS环境dockercephadm方式部署Ceph 17.2.51. 前言2. 环境准备2.1. 主机信息2.2. NTP时间同步2.3. 关闭 iptable 和 firewalld2.4. 关闭 SElinux2.5. 生成SSH证书&#xff0c;并分发到其他节点2.6. 依赖安装3. 安装部署Ceph17.2.53.1. 安装cephadm&#xff0c;拉取ce…