Day57【动态规划】647.回文子串、516.最长回文子序列

news2024/11/18 7:51:07

647.回文子串

力扣题目链接/文章讲解

视频讲解

1、确定 dp 数组下标及值含义

dp[i][j]:表示区间范围为 [i, j] 的子串是否为回文串(j >= i)

这样定义才方便我们的递推!怎么想到的?回文串需要对比串的两端,用二维 dp 数组表示串的两端元素的对比情况 

2、确定递推公式

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

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

当 s[i] == s[j],有如下三种情况:

  1. i == j,此时区间范围 [i, j] 就只有一个元素,当然是回文子串,dp[i][j] 为 true
  2. i + 1 = j,此时区间范围 [i, j] 为两个相同元素,也是回文子串,dp[i][j] 也为true
  3. i 与 j 相差大于 1 的时候,例如 cabac,此时 s[i] 与 s[j] 已经相同了,我们看区间 [i, j] 是不是回文子串就看区间 [i + 1, j - 1],即 aba 是不是回文就可以了

上述情况代码如下

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

3、dp 数组初始化

全部初始化为 false,在遍历填充过程中不断发现回文子串并置 true

4、确定遍历顺序 

根据递推公式看出,dp[i][j] 依赖于其左下方的 dp 值,所以一定要从下到上,从左到右遍历,才能保证 dp[i + 1][j - 1] 都是经过计算的 

5、打印 dp 数组验证

代码如下

class Solution {
public:
    int countSubstrings(string s) {

        vector<vector<bool> > dp(s.size(), vector<bool>(s.size(), false));

        for (int i = s.size() - 1; i >= 0; --i) {    // 从下往上,从左往右遍历
            for (int j = i; j < s.size(); ++j) {    // 保证j>=i
                if (s[i] == s[j]) {    
                    if (j - i <= 1)
                        dp[i][j] = true;
                    else if (dp[i + 1][j - 1] == true)
                        dp[i][j] = true;
                }
            }
        }

        int res = 0;    // dp[i][j]为true,就表示子串[i, j]为回文子串,统计dp数组中true的数量即可统计出回文子串的数量
        for (const auto & line : dp)
            for (const auto item : line)
                if (item == true)
                    ++res;
        
        return res;
    }
};

516.最长回文子序列 

力扣题目链接/文章讲解

视频讲解

1、定义 dp 数组下标及值含义

dp[i][j]:表示字符串 s 在 [i, j] 范围内的最长回文子序列的长度为 dp[i][j](j >= i)

回文序列需要对比序列的两端,用二维 dp 数组表示两端元素的对比情况

2、确定递推公式

关键逻辑就是看 s[i] 与 s[j] 是否相同

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

如果 s[i] != s[j],则 s[i, j] 的最长回文子序列不可能同时包含 s[i] 和 s[j],那么,长度一定为下面两种情况之一

  • 只考虑在 s[i, j - 1] 中寻找最长回文子序列,这样能够保证找到的回文子序列不可能同时包含 s[i] 和 s[j]
  • 同理,也可以只考虑在 s[i + 1, j] 中找最长回文子序列,这样也能保证找到的回文子序列不可能同时包含 s[i] 和 s[j]

因为找的“最长”回文子序列,上面两种情况取一个最大值,如图 

3、dp 数组初始化

感觉代码随想录讲得不够清晰,我们来逐步推导确定有哪些部分我们需要初始化,以及应该初始化为多少

根据定义我们看出,dp[i][j]:表示字符串 s 在 [i, j] 范围内的最长回文子序列的长度,其中 j >= i

那么,最终我们的 dp 数组也只需要去遍历填充 j >= i 的部分,因为其他部分没有意义

又根据递推公式看出,我们想要推导 dp[i. j], 需要依赖于其左方、下方、左下方的 dp 值

因此,为了能够遍历填充满绿色部分,我们需要初始化红色对角线部分与紫色部分的值,如下图所示 

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

紫色部分的 dp[i][j]:没有意义。对于这种没有实际意义的不知道如何初始化的,可以根据递推公式判断应该初始化为多少 

哪里用到了紫色部分的值?看图,当 j = i + 1 时,如果 s[i] == s[j],那么 dp[i][j] = dp[i + 1][j - 1] + 2,这个时候会用到紫色部分的 dp 值。显然,当 j = i + 1 且 s[i] == s[j] 时,即:两个相同字符构成串的最长回文子序长度就是 2,再带回递推公式,可以看出紫色部分应该初始化为 0

其他部分随意初始化,反正会被覆盖

4、确定遍历顺序

从下往上遍历 i,从左往右遍历 j,这样才能保证 dp[i][j] 所依赖的数据是被更新后的正确 dp 值

5、打印 dp 数组验证

代码如下

class Solution {
public:
    int longestPalindromeSubseq(string s) {

        vector<vector<int> > dp(s.size(), vector<int>(s.size(), 123));  // 这里初始化为123表示“其他部分可以随意初始化”

        for (int i = 0; i < s.size(); ++i) {    // 初始化对角线
            dp[i][i] = 1;
        }

        for (int i = 1; i < s.size(); ++i) {    // 初始化紫色部分
            dp[i][i - 1] = 0;
        }

        for (int i = s.size() - 2; 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]; // 表示字符串s在[0, s.size()-1]范围内的最长回文子序列的长度
    }
};

回顾总结 

动态规划结束,定义 dp 数组和递推是关键

动态规划五部曲贯穿始终 

  1. 确定 dp 数组下标及值的含义
  2. 确定递推公式
  3. dp 数组如何初始化
  4. 确定遍历顺序
  5. 打印 dp 数组验证

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

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

相关文章

【大数据学习篇11】Spark项目实战~网站转化率统计

学习目标/Target 掌握广告点击流实时统计实现思路 掌握利用Kafka生产用户广告点击流数据 了解数据库设计 掌握如何创建Spark Streaming连接 掌握利用Spark Streaming读取业务数据 掌握利用Spark读取黑名单用户 掌握利用Spark Streaming过滤黑名单用户 掌握利用Spark St…

利用ArcGIS与CAD制作设计底图

准备工作&#xff1a; 1、需要用到的软件&#xff1a;ArcGIS软件、AutoCAD&#xff1b; 2、卫星图数据、矢量数据&#xff08;因数据涉密&#xff0c;需要的同事请联系科技小组拷贝&#xff0c;并签署保密协议&#xff0c;严格履行保密责任&#xff09;。 现在&#xff0c;保…

小程序开发逆势爆发,如此会无疾而终?

2019年&#xff0c;小程序迎来了爆发式的增长&#xff0c;一年时间&#xff0c;微信小程序的活跃用户达到了3.2亿&#xff0c;日活跃用户高达5.4亿&#xff0c;在这巨大的数据背后&#xff0c;是无数商家和企业的努力与付出。小程序开发的优势显而易见&#xff0c;不少商家和企…

for in和for of的区别

for in for in 使用于可枚举的数据 如 对象 数组 字符串 什么是可枚举的&#xff1a;属性的enumerable值为true&#xff0c;表示可枚举 可以通过es7新增的属性 Object.getOwnPropertyDescriptors()验证 查看 Object.getOwnPropertyDescriptor() 方法用于 返回 指定 对象 上一个…

kubernetes安装dashboard教程

kubernetes安装dashboard教程 前提&#xff1a; kubernetes集群安装完毕 安装&#xff1a; 1.到github获取配置文件 github下面给出方法说使用下面的直接执行就可以了&#xff0c;但是最近不知道为何找不到地址。 kubectl apply -f https://raw.githubusercontent.com/ku…

查看网页cookie的方法

方法一 进入目标网页后&#xff0c; 按F12&#xff0c;找到Console&#xff0c;在filter框内输入&#xff1a;document.cookie&#xff0c;然后回车 如果filter框内输入后下面没有显示&#xff0c;需要在2的位置输入document.cookie回车 其中红色的内容即为cookie内容 不过这…

如何使用OpenAI GPT-3进行自然语言生成?

自然语言生成是一项非常引人注目的技术&#xff0c;可以让计算机像人类一样理解、生成自然语言文本。最近&#xff0c;OpenAI发布了一种名为GPT-3的巨型语言模型&#xff0c;它是史上最强大的自然语言生成模型之一。在本文中&#xff0c;我将介绍如何使用GPT-3进行自然语言生成…

vue-admin-template后台管理模板在windows/linux/maxos使用

能用克隆与编译运行命令: # 克隆项目 git clone https://github.com/PanJiaChen/vue-admin-template.git# 进入项目目录 cd vue-admin-template# 安装依赖 npm install# 开发者模式运行 npm run dev 1. macos: 降级为NODEJS 16运行工程: export NODE_OPTIONS--openssl-legac…

【服务器】本地搭建PHP简单Imagewheel私人云图床

文章目录 1.前言2. Imagewheel网站搭建2.1. Imagewheel下载和安装2.2. Imagewheel网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar临时数据隧道3.2.Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 4.公网访问测…

AndroidStudio Logcat中文乱码

1&#xff1a;Help-Edit Custom VM Options...&#xff0c;添加&#xff1a; -Dfile.encodingUTF-8 2&#xff1a;File-Settings....-Edittor-File Encodings,Global Encoding、Project Encoding设置为UTF-8 3&#xff1a;记得一定要重启AndroidStudio才会生效。

【Servlet编程】使用Smart Tomcat插件运行Servlet程序

前言: 大家好,我是良辰丫,在上一篇文章中我们已经学习了部署我们的第一个Servlet程序,想必大家对各个步骤已经有了一定的了解和认识,那么能不能优化一下各个步骤呢?每次打包部署有点麻烦哦!那么今天我们就来学习一个idea的插件,可以帮助我们简化我们的部署操作!!!&#x1f49e…

如何在Microsoft Excel中使用COUNTIF函数

COUNTIF 是一个 Excel 函数,用于对满足单个条件的区域中的单元格进行计数。COUNTIF可用于计算包含日期、数字和文本的单元格。COUNTIF 中使用的条件支持逻辑运算符(>、<、<>、=)和通配符(*、?)进行部分匹配。 例如,我们想计算包含 Google或 Facebook 的单元…

Android的消息机制

Android的消息机制 Android的消息机制概述 Android的消息机制主要指的是Handler的运行机制以及Handler所附带的MessageQueue和Looper的工作机制 Handler的主要作用是将一个任务切换到某个指定的线程中执行。 它的主要用处就是当要更新UI界面的时候,我们不能在非UI线程进行更…

React学习笔记九-高阶函数与函数柯里化

此文章是本人在学习React的时候&#xff0c;写下的学习笔记&#xff0c;在此纪录和分享。此为第九篇&#xff0c;主要介绍高阶函数与函数柯里化。 高阶函数&#xff0c;和函数的柯里化&#xff0c;是学习react的拓展&#xff0c;方便以后优化代码&#xff0c;更好的学习react。…

c语言编程练习题:7-115 小于m的最大的10个素数

#include <stdio.h> int is_prime(int a){for (int i2;i<a;i){if (a%i0){return 0;}}return 1; }int main(){int n;int count10;if (scanf("%d",&n)!EOF && n>50 && n<20000){// 计算150&#xff0c;分配给5&#xff0c;2&#x…

UOS桌面系统开机进入Busybox

UOS桌面系统开机进入Busybox 一、问题现象二、解决方案1、livecd工具修复a、制作livecd工具盘b、从优盘启动c、磁盘修复 2、使用fsck修复a、找出有问题的分区b、修复分区c、重启电脑 一、问题现象 开机进入如下图所示界面 问题原因&#xff1a;roota分区损坏 二、解决方案 …

MySQL — InnoDB引擎、MySQL架构、事务原理、MVCC

文章目录 InnoDB引擎一、逻辑存储架构二、架构2.1 内存结构2.1.1 Buffer Pool 缓冲池2.1.2 Change Buffer 更改缓冲区2.1.3 Log Buffer 日志缓冲区域2.1.4 Adaptive Hash Index 自适应hash索引 2.2 磁盘结构2.2.1 System Tablespace 系统表空间2.2.2 File-Per-Table Tablespace…

搭建一个vuepress静态网站及配置

搭建一个vuepress静态网站及配置 一、搭建一个vuepress网站1、创建并进入一个新目录2、初始化3、安装依赖4、创建文档5、配置启动命令及启动6、展示效果 二、配置及丰富vuepress网站1、增加配置文件2、配置侧边栏目录3、使用部分markdown语法完善页面 一、搭建一个vuepress网站…

【Python实战】Python采集热榜数据

前言 大家好,我们今天来爬取热搜榜,把其文章名称,链接和作者获取下来,我们保存到本地,我们通过测试,发现其实很简单,我们只要简单获取数据就可以。没有加密的东西。 效果如下: 环境使用 python 3.9pycharm模块使用 requests模块介绍 requests requests是一个很…

​​​​Linux Shell 实现一键部署Ruby3

ruby Ruby&#xff0c;一种简单快捷的面向对象&#xff08;面向对象程序设计&#xff09;脚本语言&#xff0c;在20世纪90年代由日本人松本行弘(Yukihiro Matsumoto)开发&#xff0c;遵守GPL协议和Ruby License。它的灵感与特性来自于 Perl、Smalltalk、Eiffel、Ada以及 Lisp …