代码随想录67——额外题目【动态规划】:5最长回文子串、132分割回文串II、673最长递增子序列的个数

news2024/11/26 20:49:07

文章目录

  • 1.5最长回文子串
    • 1.1.题目
    • 1.2.解答
  • 2.132分割回文串II
    • 2.1.题目
    • 2.2.解答
  • 3.673最长递增子序列的个数
    • 3.1.题目
    • 3.2.解答

1.5最长回文子串

参考:代码随想录,5最长回文子串;力扣题目链接

1.1.题目

在这里插入图片描述

1.2.解答

本题和 647.回文子串 差不多是一样的,但 647.回文子串 更基本一点,建议可以先做647.回文子串。

动规五部曲

1.确定dp数组(dp table)以及下标的含义

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

2.确定递推公式

在确定递推公式时,就要分析如下几种情况。

整体上是两种,就是s[i]s[j]相等,s[i]s[j]不相等这两种。

  • s[i]s[j]不相等,那没啥好说的了,dp[i][j]一定是false

  • s[i]s[j]相等时,这就复杂一些了,有如下三种情况

(1)情况一:下标ij相同,同一个字符例如a,当然是回文子串
(2)情况二:下标i j相差为1,例如aa,也是文子串
(3)情况三:下标:i j相差大于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) { // 情况一 和 情况二
        dp[i][j] = true;
    } else if (dp[i + 1][j - 1]) { // 情况三
        dp[i][j] = true;
    }
}

注意这里没有列出当s[i]s[j]不相等的时候,因为在下面dp[i][j]初始化的时候,就初始为false

在得到[i,j]区间是否是回文子串的时候,直接保存最长回文子串的左边界和右边界,代码如下:

if (s[i] == s[j]) {
    if (j - i <= 1) { // 情况一 和 情况二
        dp[i][j] = true;
    } else if (dp[i + 1][j - 1]) { // 情况三
        dp[i][j] = true;
    }
}
if (dp[i][j] && j - i + 1 > maxlenth) {
    maxlenth = j - i + 1;
    left = i;
    right = j;
}

3.dp数组如何初始化

dp[i][j]可以初始化为true么? 当然不行,怎能刚开始就全都匹配上了。

所以dp[i][j]初始化为false

4.确定遍历顺序

首先从递推公式中可以看出,情况三是根据dp[i + 1][j - 1]是否为true,在对dp[i][j]进行赋值true的。dp[i + 1][j - 1]dp[i][j]的左下角,如图:
在这里插入图片描述
所以一定要从下到上,从左到右遍历,这样保证dp[i + 1][j - 1]都是经过计算的。

有的代码实现是优先遍历列,然后遍历行,其实也是一个道理,都是为了保证dp[i + 1][j - 1]都是经过计算的。

5.举例推导dp数组

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

在这里插入图片描述

最后给出整体代码如下:

string longestPalindrome(string s)
{
    // 1.定义dp数组并初始化为false
    vector<vector<bool>> dp(s.size(), vector<bool>(s.size(), false));
    int max_len = 0;   // 最长回文子串的长度
    int left = 0;
    int right = 0;

    // 2.开始动态规划:从下往上、从左往右进行遍历
    for(int i = s.size()-1; i >= 0; i--)
    {
        for(int j = i; j < s.size(); j++)
        {
            // 2.1.不相等:则肯定就是false
            if(s[i] != s[j])
            {
                dp[i][j] = false;   // 这一句不加也行
            }
            // 2.2.相等:则要分情况讨论
            else
            {
                if(i == j)   // 一个字符长度,是回文
                    dp[i][j] = true;
                else if(j - i == 1)  // 两个字符,也是回文
                    dp[i][j] = true;
                else
                    dp[i][j] = dp[i+1][j-1];  // 取决于内部的字符
            }

            // 如果是回文串,计算是否是最大长度的子串
            if(dp[i][j] && j-i+1 > max_len)
            {
                max_len = j - i + 1;
                left = i;
                right = j;
            }
        }
    }

    // 截取最长回文子串返回:开始位置,长度
    return s.substr(left, right-left+1);    
}

2.132分割回文串II

参考:代码随想录,132分割回文串II;力扣题目链接

2.1.题目

在这里插入图片描述

2.2.解答

关于回文子串,两道题目题目是一定要掌握的。

  • 647.回文子串
  • 5.最长回文子串,这道题和 647.回文子串 基本一样的

这两道题目是回文子串的基础题目,本题也要用到相关的知识点。

动规五部曲分析如下:

1.确定dp数组(dp table)以及下标的含义

dp[i]:范围是[0, i]的回文子串,最少分割次数是dp[i]

2.确定递推公式

来看一下由什么可以推出dp[i]

  • 首先如果长度为[0, i]的子串本身就是回文串了,那么本着要求最少分割的回文串的目的出发,显然就不需要对它进行分割了,所以它的最少分割次数为0

  • 如果要对长度为[0, i]的子串进行分割,分割点为j。讨论如下:

如果分割后,区间[j + 1, i]是回文子串,那么dp[i] 就等于 dp[j] + 1

这里可能有同学就不明白了,为什么只看[j + 1, i]区间,不看[0, j]区间是不是回文子串呢?

那么在回顾一下dp[i]的定义: 范围是[0, i]的回文子串,最少分割次数是dp[i]

[0, j]区间的最小切割数量,我们已经知道了就是dp[j]

此时就找到了递推关系,当切割点j在[0, i] 之间时候,dp[i] = dp[j] + 1;

本题是要找到最少分割次数,所以遍历j的时候要取最小的dp[i]

所以最后递推公式为:dp[i] = min(dp[i], dp[j] + 1);

注意这里不是要 dp[j] + 1 和 dp[i]去比较,而是要在遍历j的过程中取最小的dp[i]!

可以有dp[j] + 1推出,当[j + 1, i] 为回文子串

3.dp数组如何初始化

  • 首先来看一下dp[0]应该是多少。

dp[i]: 范围是[0, i]的回文子串,最少分割次数是dp[i]

那么dp[0]一定是0,长度为1的字符串最小分割次数就是0。这个是比较直观的。

  • 再看一下非零下标的dp[i]应该初始化为多少?

在递推公式dp[i] = min(dp[i], dp[j] + 1) 中我们可以看出每次要取最小的dp[i]

那么非零下标的dp[i]就应该初始化为一个最大数,这样递推公式在计算结果的时候才不会被初始值覆盖!

如果非零下标的dp[i]初始化为0,在那么在递推公式中,所有数值将都是零。

代码如下:

vector<int> dp(s.size(), INT_MAX);
dp[0] = 0;

其实也可以这样初始化,更具dp[i]的定义,dp[i]的最大值其实就是i也就是把每个字符分割出来

所以初始化代码也可以为:

vector<int> dp(s.size());
for (int i = 0; i < s.size(); i++) dp[i] = i;

4.确定遍历顺序

根据递推公式:dp[i] = min(dp[i], dp[j] + 1);

j是在[0,i]之间,所以遍历ifor循环一定在外层,这里遍历jfor循环在内层才能通过 计算过的dp[j]数值推导出dp[i]

代码如下:

for (int i = 1; i < s.size(); i++) {
    if (isPalindromic[0][i]) { // 判断是不是回文子串
        dp[i] = 0;
        continue;
    }
    for (int j = 0; j < i; j++) {
        if (isPalindromic[j + 1][i]) {
            dp[i] = min(dp[i], dp[j] + 1);
        }
    }
}

大家会发现代码里有一个isPalindromic函数,这是一个二维数组isPalindromic[i][j]记录[i, j]是不是回文子串

所以先用一个二维数组来保存整个字符串的回文情况,这个和前面做的 5.最长回文子串 的题目是一样的。

代码如下:

vector<vector<bool>> isPalindromic(s.size(), vector<bool>(s.size(), false));
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 || isPalindromic[i + 1][j - 1])) {
            isPalindromic[i][j] = true;
        }
    }
}

5.举例推导dp数组

以输入:"aabc" 为例:
在这里插入图片描述

最后给出代码如下:

int minCut(string s)
{
    // 1.先把所有子串是不是回文判断出出来
    vector<vector<bool>> is_pal(s.size(), vector<bool>(s.size(), false));
    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 || is_pal[i+1][j-1]))
                is_pal[i][j] = true;
        
    // 2.定义dp数组并初始化:初始化成最大值,这样后面递推公式才能有效
    vector<int> dp(s.size(), 0);
    for(int i = 1; i < s.size(); i++)
        dp[i] = i; 

    // 3.动态规划
    for(int i = 1; i < s.size(); i++)
    {
        if(is_pal[0][i] == true)
        {
            dp[i] = 0;
            continue;
        }

        for(int j = 0; j < i; j++)
            if(is_pal[j+1][i])
                dp[i] = min(dp[i], dp[j] + 1);
    } 

    return dp[s.size()-1];
}

3.673最长递增子序列的个数

参考:代码随想录,673最长递增子序列的个数;力扣题目链接

3.1.题目

在这里插入图片描述

3.2.解答

TODO:比较复杂,等待二刷。。。

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

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

相关文章

Codeforces Round #574 (Div. 2) C. Basketball Exercise

翻译&#xff1a; 最后&#xff0c;SIS已经开放了一个篮球场&#xff0c;所以Demid决定举办一个篮球训练课程。有2个⋅&#x1d45b;的学生参加了Demid的练习课&#xff0c;他将他们排成两排&#xff0c;大小相同(每排正好有&#x1d45b;人)。学生按从左到右的顺序&#xff0…

【毕业设计】15-基于单片机的交通灯系统设计(原理图+仿真+论文)

【毕业设计】15-基于单片机的交通灯系统设计&#xff08;原理图、仿真、源代码工程答辩论文答辩PPT&#xff09; 文章目录【毕业设计】15-基于单片机的交通灯系统设计&#xff08;原理图、仿真、源代码工程答辩论文答辩PPT&#xff09;任务书设计说明书摘要设计框架架构设计说明…

婴幼儿牛奶蛋白过敏危害多,教你四招早期预防

牛奶蛋白过敏&#xff08;cowsmilkproteinallergy&#xff0c;CMPA&#xff09;这是婴幼儿最常见的食物过敏之一。牛奶蛋白过敏的临床表现CMPA儿童的临床表现多种多样[1]&#xff0c;特别是对严重的牛奶蛋白过敏&#xff0c;会导致拒绝进食、腹泻、呕吐或反流&#xff0c;导致生…

CentOS7安装superset2.0

备注&#xff1a;自己在CentOS7.5下安装superset2.0成功。数据库以本地sqlite为准。Superset是由Python语言编写的Web应用&#xff0c;Superset2.0版本要求Python3.9的环境。 1、安装Miniconda 原因&#xff1a;conda是一个开源的包、环境管理器&#xff0c;可以用于在同一个…

Linux/Windows中创建共享文件夹

Linux/Windows中创建共享文件夹一、虚拟机访问主机的文件夹1、设置虚拟机共享主机的路径2、设置主机目录为共享文件夹【Windows之间共享目录】2.1 设置共享文件夹2.2 访问共享文件夹3、nfs_共享文件夹【linux之间共享目录】一、虚拟机访问主机的文件夹 1、设置虚拟机共享主机的…

计算机内存与外存的区别及使用配合(内存外存区别与搭配;快速缓存;计算机总线结构)

计算机系统结构1. 为什么计算机存储会分为内存和外存呢&#xff1f;2. 关于快速缓存3. 计算机总线结构1. 为什么计算机存储会分为内存和外存呢&#xff1f; 外部储存器断电可以存储数据&#xff0c;但是读写速度相对于cpu来说很慢&#xff0c;而内存虽然读取速度很快但是断电之…

【php环境搭建】php全栈体系(二)

PHP环境搭建 第七章 安装与配置MySQL 一、安装MySQL软件 1. 获取MySQL安装软件 2. 双击安装即可&#xff1a;没有特殊情况的直接下一步就可以完成 3. 选择custom&#xff0c;自定义安装&#xff1a;选择安装路径 3.1 软件安装目录&#xff1a;server/mysql 3.2 数据安装目录…

OVS DPDK VXLAN隧道处理

在学习OVS VXLAN实现之前&#xff0c;我们先回顾一下传统VTEP设备是如何处理VXLAN报文的。如下图所示&#xff1a; vxlan报文进入交换机端口后&#xff0c;根据报文头部信息进行vxlan隧道终结。隧道终结后&#xff0c;根据underlay信息进行overlay映射&#xff0c;得到overlay的…

Spark系列之SparkSubmit提交任务到YARN

title: Spark系列 第十三章 SparkSubmit提交任务到YARN 13.1 SparkSubmit提交的一些参数解释 local 本地单线程 local[K] 本地多线程&#xff08;指定K个内核&#xff09; local[*] 本地多线程&#xff08;指定所有可用内核&#xff09; spark://HOST:PORT 连接到指定的 Spar…

欢聚季报图解:营收5.87亿美元同比降10% 净利提升

雷递网 雷建平 11月29日欢聚集团(NASDAQ: YY)今日发布2022年第三季度财报。财报显示&#xff0c;欢聚集团2022年第三季度营收为5.867亿美元&#xff0c;较上年同期下降10%。欢聚集团2022年第三季度Bigo Live的平均移动MAU为3540万&#xff0c;较上年同期的3100万增长14.2%&…

固态硬盘SSD

固态硬盘或固态驱动器&#xff08;英语&#xff1a;Solid-state drive或Solid-state disk&#xff0c;简称SSD&#xff09;是一种以集成电路制作的电脑存储设备&#xff0c;由于价格及最大存储容量与机械硬盘有巨大差距&#xff0c;固态硬盘无法与机械式硬盘竞争。可以用非易失…

视频转格式用什么工具?mp4格式转换器,好用的视频格式转换器

视频转格式用什么工具&#xff1f;一条视频在不同的手机、软件上&#xff0c;所支持的格式是不同的&#xff0c;可能手机不支持部分不常见的视频格式&#xff0c;需要使用视频格式转换器将它的格式进行修改&#xff0c;就能使它匹配你所需的格式。接下来&#xff0c;小编就带大…

集成电路光刻机精密运动台控制方法

集成电路的持续发展推动了信息技术的进步。步进扫描式光刻机是集成电路装备中 技术难度最高、价格最昂贵的关键设备。其中&#xff0c;精密运动台是光刻机最为核心的子系统之一&#xff0c; 高速、高加速和高精度的要求对控制方法的研究和应用提出了挑战。文中对集成电路光刻机…

C++ Reference: Standard C++ Library reference: Containers: list: list: empty

C官网参考链接&#xff1a;https://cplusplus.com/reference/list/list/empty/ 公有成员函数 <list> std::list::empty C98 bool empty() const; C11 bool empty() const noexcept; 测试容器是否为空 返回列表&#xff08;list&#xff09;容器是否为空&#xff08;即其…

Protobuf用法和实际操作总结

Protobuf介绍 Protobuf(下文称为 PB)是一种常见的数据序列化方式&#xff0c;常常用于后台微服务之间传递数据 protobuf 的类图如下&#xff1a; 类 Descriptor 介绍 类 Descriptor 主要是对 Message 进行描述&#xff0c;包括 message 的名字、所有字段的描述、原始 proto…

SpringCloud_第1章_入门到精通()

SpringCloud_第1章_入门到精通 文章目录SpringCloud_第1章_入门到精通1.认识微服务1.0.学习目标1.1.单体架构1.2.分布式架构1.3.微服务1.4.SpringCloud1.5.总结2.服务拆分和远程调用2.1.服务拆分原则2.2.服务拆分示例2.2.1.导入Sql语句2.2.2.导入demo工程2.3.实现远程调用案例2…

几种常用XML文档解析方案的使用操作

几种常用XML文档解析方案的使用操作XML概述组成部分XML示例约束DTDSchema解析XML解析方式常见解析器JAXP的使用准备student.xml文件解析XML文档写入XML文档dom4j的使用Document对象获取方式引入依赖解析XML文档文档写入XMLJsoup的使用引入依赖核心对象解析XML文档JsoupXPath的使…

护眼灯真能保护眼睛吗?2022双十二买什么样的护眼灯对眼睛好

护眼灯对于保护眼睛的效果&#xff0c;比起传统的普通台灯更科学、更直观。我们都知道&#xff0c;传统的普通台灯大多都是白炽灯&#xff0c;采用加热钨丝发热发光的原理来提供照明&#xff0c;这种发光方式不但效率低下、浪费能源、污染环境&#xff0c;而且光线也是特别单一…

(二)DepthAI-python相关接口:OAK Pipeline

消息快播&#xff1a;OpenCV众筹了一款ROS2机器人rae&#xff0c;开源、功能强、上手简单。来瞅瞅~ 编辑&#xff1a;OAK中国 首发&#xff1a;oakchina.cn 喜欢的话&#xff0c;请多多&#x1f44d;⭐️✍ 内容可能会不定期更新&#xff0c;官网内容都是最新的&#xff0c;请查…

Word文档格式设置——小课堂

文章目录前言1、调整显示选项2、格式设置2.1、新建空白文档&#xff0c;使用默认格式添加一至六级目录&#xff1b;2.2、设置标题的样式2.3、添加标题序号2.4、设置正文格式2.5 设置表格边框2.6 设置表格段落样式2.7 插入表格题注2.9 设置图片格式&#xff0c;并插入题注3、小技…