代码随想录刷题day53|最长公共子序列不相交的线最大子序和

本文主要是介绍代码随想录刷题day53|最长公共子序列不相交的线最大子序和,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • day53学习内容
  • 一、最长公共子序列
    • 1.1、动态规划五部曲
      • 1.1.1、 确定dp数组(dp table)以及下标的含义
      • 1.1.2、确定递推公式
      • 1.1.3、 dp数组如何初始化
      • 1.1.4、确定遍历顺序
      • 1.1.5、输出结果
    • 1.2、代码
  • 二、不相交的线
    • 2.1、动态规划五部曲
      • 2.1.1、 确定dp数组(dp table)以及下标的含义
      • 2.1.2、确定递推公式
      • 2.1.3、 dp数组如何初始化
      • 2.1.4、确定遍历顺序
      • 2.1.5、输出结果
    • 2.2、代码
  • 三、最大子序和
    • 3.1、动态规划五部曲
      • 3.1.1、 确定dp数组(dp table)以及下标的含义
      • 3.1.2、确定递推公式
      • 3.1.3、 dp数组如何初始化
      • 3.1.4、确定遍历顺序
      • 3.1.5、输出结果
    • 3.2、代码
  • 总结
    • 1.感想
    • 2.思维导图


day53学习内容

day53主要内容

  • 最长公共子序列
  • 不相交的线
  • 最大子序和

声明
本文思路和文字,引用自《代码随想录》


一、最长公共子序列

1143.原题链接

1.1、动态规划五部曲

1.1.1、 确定dp数组(dp table)以及下标的含义

  • dp[i][j] 表示字符串 text1 的前 i 个字符和字符串 text2 的前 j 个字符的最长公共子序列的长度。

1.1.2、确定递推公式

基本思路
我们可以根据两个字符串中当前字符是否相等,采取不同的策略来更新 dp 表。
与最长公共子数组(或子串)不同,子序列不要求元素在原字符串中是连续的,只要求它们保持相对顺序即可。

推导状态转移方程

  1. 字符匹配的情况
    text1[i-1] == text2[j-1] 时,这意味着这两个字符可以成为目前为止考虑的字符串的公共子序列的一部分。因此,这两个字符将增加公共子序列的长度,即
dp[i][j]=dp[i−1][j−1]+1

这个方程表明,text1 的前 i 个字符和 text2 的前 j 个字符的最长公共子序列可以通过在它们的前 i-1 个字符和前 j-1 个字符的最长公共子序列的基础上添加这两个匹配的字符来得到。

  1. 字符不匹配的情况
    text1[i-1] != text2[j-1] 时,最长公共子序列不能同时包括这两个字符。因此,有以下两种可能性:
    • 忽略 text1 的当前字符 i-1,只考虑 text1 的前 i-1 个字符和 text2 的前 j 个字符。
    • 忽略 text2 的当前字符 j-1,只考虑 text1 的前 i 个字符和 text2 的前 j-1 个字符。
      这两种情况下的最长公共子序列的长度将是:
dp[i][j]=max(dp[i−1][j],dp[i][j−1])

这个方程表明,当前的 dp[i][j] 应取前面两种情况中更长的子序列长度。

1.1.3、 dp数组如何初始化

  • dp[0][x]dp[x][0] 应该初始化为 0,因为一个长度为 0 的字符串与任何字符串的最长公共子序列长度都是 0。
  • 或者这句话看不懂,就画个图。。。看一下推导的方向就明白了
    在这里插入图片描述
    图引用自卡尔的视频。

https://www.bilibili.com/video/BV1ye4y1L7CQ/?spm_id_from=pageDriver&vd_source=266f115062b99cd2d8e5185add0b8cc9

1.1.4、确定遍历顺序

从小到大遍历

1.1.5、输出结果

  • return dp[text1.length()][text2.length()]; 最后,dp 数组的最后一个元素(即 dp[text1.length()][text2.length()])包含了整个 text1text2 的最长公共子序列的长度。

1.2、代码

class Solution {public int longestCommonSubsequence(String text1, String text2) {// 创建二维数组dp,大小为text1长度+1和text2长度+1int[][] dp = new int[text1.length() + 1][text2.length() + 1];// 遍历text1的每一个字符for (int i = 1; i <= text1.length(); i++) {char char1 = text1.charAt(i - 1); // 获取text1的第i-1个字符// 遍历text2的每一个字符for (int j = 1; j <= text2.length(); j++) {char char2 = text2.charAt(j - 1); // 获取text2的第j-1个字符// 如果两个字符相同if (char1 == char2) {// 如果当前的两个字符相同,则当前dp[i][j]应基于之前的dp[i-1][j-1]的值加1dp[i][j] = dp[i - 1][j - 1] + 1;} else {// 如果两个字符不同,选择之前两种情况的较大者作为当前dp[i][j]的值dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);}}}// 返回数组的最后一个元素,即为text1和text2的最长公共子序列长度return dp[text1.length()][text2.length()];}
}

二、不相交的线

1035.原题链接

它实质上和最长公共子序列(LCS)问题相同,只是在不同的上下文中应用。在这个问题中,我们要在两个数组中找到最多的对应数字(或线),使得这些数字的顺序在两个数组中保持一致,但不必连续。

2.1、动态规划五部曲

2.1.1、 确定dp数组(dp table)以及下标的含义

  • 其中 dp[i][j] 代表考虑 nums1 的前 i 个元素和 nums2 的前 j 个元素时的最大不相交线数(或最长公共子序列的长度)。

2.1.2、确定递推公式

  • for (int i = 1; i <= len1; i++):外层循环遍历 nums1
  • for (int j = 1; j <= len2; j++):内层循环遍历 nums2
    • if (nums1[i - 1] == nums2[j - 1]):如果当前考虑的两个元素相等,表示可以在此基础上形成一个新的对应线,所以 dp[i][j] = dp[i - 1][j - 1] + 1;。这意味着我们在之前的最大对应线数的基础上增加一条新的线。
    • else:如果当前的两个元素不相等,则我们需要决定是跳过 nums1 的当前元素还是 nums2 的当前元素,以保持最大的对应线数。因此,dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); 选择这两种情况中较大的一个。

2.1.3、 dp数组如何初始化

根据前面的分析。dp 表的最后一个元素就是两个数组中可以形成的最大不相交线数,这等同于最长公共子序列的长度。所以初始化逻辑和上面那题是一样的,这里不再赘述了。

2.1.4、确定遍历顺序

从小到大遍历

2.1.5、输出结果

  • return dp[len1][len2]; 动态规划表的最后一个元素包含了整个 nums1nums2 可以形成的最大不相交线数。

2.2、代码

class Solution {// maxUncrossedLines 方法用来计算两个数组间的最大未交叉线数public int maxUncrossedLines(int[] nums1, int[] nums2) {// len1 和 len2 分别是两个数组的长度int len1 = nums1.length;int len2 = nums2.length;// dp 数组用于存储动态规划的中间结果int[][] dp = new int[len1 + 1][len2 + 1];// 外层循环遍历 nums1for (int i = 1; i <= len1; i++) {// 内层循环遍历 nums2for (int j = 1; j <= len2; j++) {// 如果当前两个数组的元素相等,更新 dp 数组的值if (nums1[i - 1] == nums2[j - 1]) {dp[i][j] = dp[i - 1][j - 1] + 1;} else {// 如果不相等,取两种情况的较大值,即不包含当前 nums1 的元素或不包含当前 nums2 的元素dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);}}}// 返回 dp 数组右下角的值,即为最大未交叉线数return dp[len1][len2];}
}

三、最大子序和

53.原题链接

3.1、动态规划五部曲

3.1.1、 确定dp数组(dp table)以及下标的含义

  • dp[i] 表示以第 i 个元素结尾的最大子数组和。

3.1.2、确定递推公式

  • 从数组的第二个元素开始遍历(即i=1)。
  • 对于每个元素nums[i],更新dp[i]。这里的转移方程是:
    • dp[i] = Math.max(dp[i-1] + nums[i], nums[i])
    • 这个方程的意思是,考虑到第i个元素时,最大子数组和可以是:
      • dp[i-1] + nums[i](即包含之前的最大子数组和再加上当前元素nums[i]),或者
      • nums[i]本身(即从新开始计数,只取当前元素,这种情况适用于dp[i-1]是负数,不如放弃之前的累计,重新开始)。
  • 同时更新结果res,如果dp[i]比当前的res还要大,就用dp[i]更新res

3.1.3、 dp数组如何初始化

dp[0]即为nums[0],因为第一个元素前面没有其他元素,所以它自身就是一个子数组。

3.1.4、确定遍历顺序

从小到大遍历

3.1.5、输出结果

最后返回 res,它存储的是整个数组的最大子数组和。

3.2、代码

class Solution {public int maxSubArray(int[] nums) {// 如果数组为空,则直接返回0(通常情况下,这里可以根据题意返回负无穷或抛出异常)if (nums.length == 0) {return 0;}// res用来存储最终的最大子数组和,初值设为数组的第一个元素int res = nums[0];// dp数组用于动态规划存储到当前元素为止的最大子数组和,dp[0]自然是第一个元素int[] dp = new int[nums.length];dp[0] = nums[0];// 从数组的第二个元素开始遍历for (int i = 1; i < nums.length; i++) {// 动态规划的转移方程:比较“继续当前子数组”与“从新的位置开始”dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);// 更新全局的最大子数组和res = Math.max(res, dp[i]);}// 返回计算得到的最大子数组和return res;}
}

总结

1.感想

  • 补周六的进度,冲

2.思维导图

本文思路引用自代码随想录,感谢代码随想录作者。

这篇关于代码随想录刷题day53|最长公共子序列不相交的线最大子序和的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/907372

相关文章

利用Python调试串口的示例代码

《利用Python调试串口的示例代码》在嵌入式开发、物联网设备调试过程中,串口通信是最基础的调试手段本文将带你用Python+ttkbootstrap打造一款高颜值、多功能的串口调试助手,需要的可以了... 目录概述:为什么需要专业的串口调试工具项目架构设计1.1 技术栈选型1.2 关键类说明1.3 线程模

Python Transformers库(NLP处理库)案例代码讲解

《PythonTransformers库(NLP处理库)案例代码讲解》本文介绍transformers库的全面讲解,包含基础知识、高级用法、案例代码及学习路径,内容经过组织,适合不同阶段的学习者,对... 目录一、基础知识1. Transformers 库简介2. 安装与环境配置3. 快速上手示例二、核心模

Java的栈与队列实现代码解析

《Java的栈与队列实现代码解析》栈是常见的线性数据结构,栈的特点是以先进后出的形式,后进先出,先进后出,分为栈底和栈顶,栈应用于内存的分配,表达式求值,存储临时的数据和方法的调用等,本文给大家介绍J... 目录栈的概念(Stack)栈的实现代码队列(Queue)模拟实现队列(双链表实现)循环队列(循环数组

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

使用Python实现全能手机虚拟键盘的示例代码

《使用Python实现全能手机虚拟键盘的示例代码》在数字化办公时代,你是否遇到过这样的场景:会议室投影电脑突然键盘失灵、躺在沙发上想远程控制书房电脑、或者需要给长辈远程协助操作?今天我要分享的Pyth... 目录一、项目概述:不止于键盘的远程控制方案1.1 创新价值1.2 技术栈全景二、需求实现步骤一、需求

Java中Date、LocalDate、LocalDateTime、LocalTime、时间戳之间的相互转换代码

《Java中Date、LocalDate、LocalDateTime、LocalTime、时间戳之间的相互转换代码》:本文主要介绍Java中日期时间转换的多种方法,包括将Date转换为LocalD... 目录一、Date转LocalDateTime二、Date转LocalDate三、LocalDateTim

jupyter代码块没有运行图标的解决方案

《jupyter代码块没有运行图标的解决方案》:本文主要介绍jupyter代码块没有运行图标的解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录jupyter代码块没有运行图标的解决1.找到Jupyter notebook的系统配置文件2.这时候一般会搜索到

Python通过模块化开发优化代码的技巧分享

《Python通过模块化开发优化代码的技巧分享》模块化开发就是把代码拆成一个个“零件”,该封装封装,该拆分拆分,下面小编就来和大家简单聊聊python如何用模块化开发进行代码优化吧... 目录什么是模块化开发如何拆分代码改进版:拆分成模块让模块更强大:使用 __init__.py你一定会遇到的问题模www.

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La