当前位置: 首页 > news >正文

# 代码随想录算法训练营Day37 | Leetcode300.最长递增子序列、674.最长连续递增序列、718.最长重复子数组

代码随想录算法训练营Day37 | Leetcode300.最长递增子序列、674.最长连续递增序列、718.最长重复子数组

一、最长递增子序列

相关题目:Leetcode300
文档讲解:Leetcode300
视频讲解:Leetcode300

1. Leetcode300.最长递增子序列

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
提示:

  • 1 <= nums.length <= 2500
  • -104 <= nums[i] <= 104
  • 思路:

    • 动规五部曲
      • 确定 dp 数组以及下标的含义:dp[i] 表示 i 前包括 i 的以 nums[i] 结尾的最长递增子序列的长度。定义以 nums[i] 结尾是因为做递增比较的时候,如果比较 nums[j] 和 nums[i] 的大小,那么两个递增子序列一定分别以 nums[j] 和 nums[i] 为结尾, 不是尾部元素的比较难以算递增。
      • 状态转移方程:位置 i 的最长升序子序列等于 j 从 0 到 i-1 各个位置的最长升序子序列 + 1 的最大值。所以:if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1)
      • dp[i] 的初始化:对每一个 i,对应的 dp[i](即最长递增子序列)起始大小至少都是 1。
      • 确定遍历顺序:dp[i] 是由 0 到 i-1 各个位置的最长递增子序列 推导而来,那么遍历 i 一定是从前向后遍历。j 只要把 0 到 i-1 的元素都遍历了就行,无论是从前到后还是从后到前遍历都可以(默认从前向后遍历)。
      • 举例推导 dp 数组:输入:[0,1,0,3,2],dp 数组的变化如下:
        请添加图片描述
  • 动规

class Solution:def lengthOfLIS(self, nums: List[int]) -> int:if len(nums) <= 1:return len(nums)dp = [1] * len(nums)result = 1for i in range(1, len(nums)):for j in range(0, i):if nums[i] > nums[j]:dp[i] = max(dp[i], dp[j] + 1)result = max(result, dp[i]) #取长的子序列return result

二、最长连续递增序列

相关题目:Leetcode674
文档讲解:Leetcode674
视频讲解:Leetcode674

1. Leetcode674.最长连续递增序列

给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。
连续递增的子序列 可以由两个下标 l 和 r(l < r)确定,如果对于每个 l <= i < r,都有 nums[i] < nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], …, nums[r - 1], nums[r]] 就是连续递增子序列。
提示:

  • 1 <= nums.length <= 104
  • -109 <= nums[i] <= 109
  • 思路:
    • 动规五部曲
      • 确定 dp 数组以及下标的含义:dp[i] 表示以下标 i 为结尾的连续递增的子序列长度为 dp[i]。(以下标 i 为结尾,并不是说一定以下标 0 为起始位置)
      • 确定递推公式:如果 nums[i] > nums[i - 1],那么以 i 为结尾的连续递增的子序列长度 一定等于以 i - 1 为结尾的连续递增的子序列长度 + 1 ,所以 dp[i] = dp[i - 1] + 1
      • dp 数组如何初始化:以下标 i 为结尾的连续递增的子序列长度最少也应该是 1,即 nums[i] 这一个元素。所以 dp[i] 皆应初始为 1。
      • 确定遍历顺序:从递推公式可以看出 dp[i + 1] 依赖 dp[i],所以一定是从前向后遍历。
      • 举例推导 dp 数组:以输入 nums = [1,3,5,4,7] 为例,dp 数组状态如下:
  • 注意:
    • 与 Leetcode300.最长递增子序列 不同,本题要求连续递增子序列,所以就只要比较 nums[i] 与 nums[i - 1],而不需比较 nums[j] 与 nums[i] (j 在 0 到 i 之间遍历)。
      请添加图片描述
  • 动规
###DP
class Solution:def findLengthOfLCIS(self, nums: List[int]) -> int:if len(nums) == 0:return 0result = 1dp = [1] * len(nums)for i in range(len(nums)-1):if nums[i+1] > nums[i]: #连续记录dp[i+1] = dp[i] + 1result = max(result, dp[i+1])return result###DP(优化版)
class Solution:def findLengthOfLCIS(self, nums: List[int]) -> int:if not nums:return 0max_length = 1current_length = 1for i in range(1, len(nums)):if nums[i] > nums[i - 1]:current_length += 1max_length = max(max_length, current_length)else:current_length = 1return max_length
  • 贪心
class Solution:def findLengthOfLCIS(self, nums: List[int]) -> int:if len(nums) == 0:return 0result = 1 #连续子序列最少也是1count = 1for i in range(len(nums)-1):if nums[i+1] > nums[i]: #连续记录count += 1else: #不连续,count从头开始count = 1result = max(result, count)return result

三、最长重复子数组

相关题目:Leetcode718
文档讲解:Leetcode718
视频讲解:Leetcode718

1. Leetcode718.最长重复子数组

给两个整数数组 nums1 和 nums2 ,返回 两个数组中 公共的 、长度最长的子数组的长度 。
提示:

  • 1 <= nums1.length, nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 100
  • 思路:
    • 题目中说的子数组其实就是连续子序列。要求两个数组中最长重复子数组,如果是暴力的解法需要先两层 for 循环确定两个数组起始位置,然后再来一个循环可以是 for 或者 while,来从两个起始位置开始比较,取得重复子数组的长度。如果利用动态规划,利用二维数组可以记录两个字符串的所有比较情况。
    • 动规五部曲
      • 确定 dp 数组以及下标的含义:dp[i][j] 表示以下标 i - 1 为结尾的 A,和以下标 j - 1 为结尾的 B,最长重复子数组长度为 dp[i][j]。
      • 确定递推公式:根据 dp[i][j] 的定义,dp[i][j] 的状态只能由 dp[i - 1][j - 1] 推导出来。即当 A[i - 1] 和 B[j - 1] 相等的时候,dp[i][j] = dp[i - 1][j - 1] + 1。
      • dp 数组如何初始化:根据 dp[i][j] 的定义,dp[i][0] 和 dp[0][j] 没有意义,但 dp[i][0] 和 dp[0][j] 要初始值,为了方便递归公式 dp[i][j] = dp[i - 1][j - 1] + 1,所以 dp[i][0] 和 dp[0][j] 初始化为 0。例:如果 A[0] 和 B[0] 相同的话,dp[1][1] = dp[0][0] + 1,只有 dp[0][0] 初始为 0,正好符合递推公式逐步累加起来。
      • 确定遍历顺序:外层 for 循环遍历 A,内层 for 循环遍历 B 或是 外层 for 循环遍历 B,内层 for 循环遍历 A 都可以。
      • 举例推导 dp 数组:以 A: [1,2,3,2,1],B: [3,2,1,4,7] 为例,dp 数组的状态变化如下:
        请添加图片描述
  • 注意:
    • 前述 dp[i][j] 的定义决定着在遍历 dp[i][j] 的时候 i 和 j 都要从1开始。
    • 定义 dp[i][j] 为以下标 i 为结尾的 A,和以下标 j 为结尾的 B,最长重复子数组长度也是可行的,但需要单独处理初始化部分:
      • 第一行和第一列需要进行初始化,如果 nums1[i] 与 nums2[0] 相同的话,对应的 dp[i][0] 要初始为 1, 因为此时最长重复子数组为 1;nums2[j] 与 nums1[0] 相同的话,同理。
    • 利用滚动数组(一维 dp):可以看出 dp[i][j] 都是由 dp[i - 1][j - 1] 推出,那么压缩可以一维数组,也就是 dp[j] 都是由 dp[j - 1] 推出。相当于可以把上一层 dp[i - 1][j] 拷贝到下一层 dp[i][j] 来继续用。此时遍历 B 数组的时候,就要从后向前遍历,避免重复覆盖。例(同上):
      请添加图片描述
  • 二维 dp
###2维DP
class Solution:def findLength(self, nums1: List[int], nums2: List[int]) -> int:# 创建一个二维数组 dp,用于存储最长公共子数组的长度dp = [[0] * (len(nums2) + 1) for _ in range(len(nums1) + 1)]# 记录最长公共子数组的长度result = 0# 遍历数组 nums1for i in range(1, len(nums1) + 1):# 遍历数组 nums2for j in range(1, len(nums2) + 1):# 如果 nums1[i-1] 和 nums2[j-1] 相等if nums1[i - 1] == nums2[j - 1]:# 在当前位置上的最长公共子数组长度为前一个位置上的长度加一dp[i][j] = dp[i - 1][j - 1] + 1# 更新最长公共子数组的长度if dp[i][j] > result:result = dp[i][j]# 返回最长公共子数组的长度return result###2维DP 扩展
class Solution:def findLength(self, nums1: List[int], nums2: List[int]) -> int:# 创建一个二维数组 dp,用于存储最长公共子数组的长度dp = [[0] * (len(nums2) + 1) for _ in range(len(nums1) + 1)]# 记录最长公共子数组的长度result = 0# 对第一行和第一列进行初始化for i in range(len(nums1)):if nums1[i] == nums2[0]:dp[i + 1][1] = 1for j in range(len(nums2)):if nums1[0] == nums2[j]:dp[1][j + 1] = 1# 填充dp数组for i in range(1, len(nums1) + 1):for j in range(1, len(nums2) + 1):if nums1[i - 1] == nums2[j - 1]:# 如果 nums1[i-1] 和 nums2[j-1] 相等,则当前位置的最长公共子数组长度为左上角位置的值加一dp[i][j] = dp[i - 1][j - 1] + 1if dp[i][j] > result:# 更新最长公共子数组的长度result = dp[i][j]# 返回最长公共子数组的长度return result
  • 一维 dp
class Solution:def findLength(self, nums1: List[int], nums2: List[int]) -> int:# 创建一个一维数组 dp,用于存储最长公共子数组的长度dp = [0] * (len(nums2) + 1)# 记录最长公共子数组的长度result = 0# 遍历数组 nums1for i in range(1, len(nums1) + 1):# 用于保存上一个位置的值prev = 0# 遍历数组 nums2for j in range(1, len(nums2) + 1):# 保存当前位置的值,因为会在后面被更新current = dp[j]# 如果 nums1[i-1] 和 nums2[j-1] 相等if nums1[i - 1] == nums2[j - 1]:# 在当前位置上的最长公共子数组长度为上一个位置的长度加一dp[j] = prev + 1# 更新最长公共子数组的长度if dp[j] > result:result = dp[j]else:# 如果不相等,将当前位置的值置为零dp[j] = 0# 更新 prev 变量为当前位置的值,供下一次迭代使用prev = current# 返回最长公共子数组的长度return result

相关文章:

  • 在 MySQL 中,索引前缀长度为什么选择为 191
  • Java24新增特性
  • OpenHarmony 开源鸿蒙北向开发——hdc工具使用及常用命令(持续更新)
  • 504 nginx解决方案
  • 机器学习基础 - 分类模型之SVM
  • “Daz to Unreal”将 G8 角色(包括表情)从 daz3d 导入到 UE5。在 UE5 中,我发现使用某个表情并与闭眼混合后,上眼睑出现了问题
  • PostgreSQL性能优化实用技巧‌
  • NLP高频面试题(五十二)——深度学习优化器详解
  • Java面试实战:电商场景下的Spring Cloud微服务架构与缓存技术剖析
  • 【hadoop】HBase shell 操作
  • NODE_OPTIONS=--openssl-legacy-provider vue-cli-service serve
  • Pikachu靶场-unsafe upfileupload
  • ASP.NET Core 主机模型详解:Host、WebHost与WebApplication的对比与实践【代码之美】
  • 微信小程序根据图片生成背景颜色有效果图
  • 【前端】【业务场景】【面试】在前端开发中,如何实现实时数据更新,比如实时显示服务器推送的消息,并且保证在不同网络环境下的稳定性和性能?
  • MCP开发实战(一)基于MCP协议的大模型网关——多个大模型API统一封装为标准化工具
  • 第六章:安全最佳实践
  • MMsegmentation第一弹-(认识与安装)
  • 试水低代码平台Nocoly
  • DeepSeek-R1: LLMs 通过强化学习激励推理能力
  • 《不眠之夜》上演8年推出特别版,多业态联动形成戏剧经济带
  • 又双叒叕出差太空了!神二十成功出发,神十九乘组扫榻以待
  • 叶迪奇任陆金所控股董事长,赵容奭继续担任CEO
  • 北朝时期的甲胄
  • 钟芳玲|戴耳环的莎士比亚
  • 海南医科大学继续开展部门正职竞聘上岗,致力营造“谁有本事谁来”