news 2026/5/1 20:04:34

代码随想录算法训练营第三十七天:最长递增子序列,最长连续递增数列,最长重复子序列

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
代码随想录算法训练营第三十七天:最长递增子序列,最长连续递增数列,最长重复子序列

300.最长递增子序列

文章讲解/视频讲解

题目描述:

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

示例 1:

  • 输入:nums = [10,9,2,5,3,7,101,18]
  • 输出:4
  • 解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

示例 2:

  • 输入:nums = [0,1,0,3,2,3]
  • 输出:4

示例 3:

  • 输入:nums = [7,7,7,7,7,7,7]
  • 输出:1

提示:

  • 1 <= nums.length <= 2500
  • -10^4 <= nums[i] <= 104

思路:
1.dp数组及其下标含义:这是本题的重中之重,本题的dp[i]表示i之前包括nums[i]结尾的最长递增子序列

2.状态转移方程:位置i的最长递增序列,等于 j 从 0 到 i - 1的最长递增序列 + 1(位置i本身),也就是我们去找最大的dp[j] + 1,

if (nums[i] > nums[j]) {

dp[i] = Math.max(dp[i], dp[j] + 1)

}

3.dp数组的初始化:所有位置最开始起码都是1,所以全部初始化为1

4.确定遍历顺序:本题一共有两层for循环,外层遍历i,内层遍历j,外层遍历的i是一定要从小到大遍历的,要不然拿不到前面的数据,内层的j无所谓从前还是从后,只要是0到i - 1这个范围就行了

5.举例推导dp数组

输入:[0,1,0,3,2],dp数组的变化如下:

如果代码写出来,但一直AC不了,那么就把dp数组打印出来,看看对不对!

代码示例:

function lengthOfLIS(nums: number[]): number { const length: number = nums.length const dp: number[] = new Array(length).fill(1) let res: number = 0 for (let i = 0; i < length; i++) { for (let j = 0; j < i; j++) { if (nums[i] > nums[j]) { dp[i] = Math.max(dp[i], dp[j] + 1) } } res = Math.max(res, dp[i]) } return res };

674.最长连续递增数列

文章讲解/视频讲解

题目描述:

给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。

连续递增的子序列 可以由两个下标 l 和 r(l < r)确定,如果对于每个 l <= i < r,都有 nums[i] < nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], ..., nums[r - 1], nums[r]] 就是连续递增子序列。

示例 1:

  • 输入:nums = [1,3,5,4,7]
  • 输出:3
  • 解释:最长连续递增序列是 [1,3,5], 长度为3。尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为 5 和 7 在原数组里被 4 隔开。

示例 2:

  • 输入:nums = [2,2,2,2,2]
  • 输出:1
  • 解释:最长连续递增序列是 [2], 长度为1。

提示:

  • 0 <= nums.length <= 10^4
  • -10^9 <= nums[i] <= 10^9

思路:

本题相比上一题其实跟像退阶版本,因为连续意味着我们不必比较nums[j]和nums[i],只需要比较nums[i]和nums[i -1]的大小即可

1.确定dp数组及其下标含义:dp[i]表示以i为结尾的连续递增子序列为dp[i]

2.确定递推公式:如果 nums[i] > nums[i - 1],那么以 i 为结尾的连续递增的子序列长度 一定等于 以i - 1为结尾的连续递增的子序列长度 + 1 。即:dp[i] = dp[i - 1] + 1

3.dp数组的初始化:虽然是连续递增子序列,但是每个序列开始时起码都为1,依旧全部初始化为1

4.确定遍历顺序:由于dp[i]依赖于dp[i - 1],所以一定是从前往后遍历的

5.举例推导dp数组

已输入nums = [1,3,5,4,7]为例,dp数组状态如下:

注意这里要取dp[i]里的最大值,所以dp[2]才是结果!

代码示例:

function findLengthOfLCIS(nums: number[]): number { const length: number = nums.length const dp: number[] = new Array(length).fill(1) let res: number = 1 for (let i = 1; i < length; i++) { if (nums[i] > nums[i - 1]) { dp[i] = dp[i - 1] + 1 } res = Math.max(res, dp[i]) } return res };

718.最长重复子数组

文章讲解/视频讲解

题目描述:

给两个整数数组 A 和 B ,返回两个数组中公共的、长度最长的子数组的长度。

示例:

输入:

  • A: [1,2,3,2,1]
  • B: [3,2,1,4,7]
  • 输出:3
  • 解释:长度最长的公共子数组是 [3, 2, 1] 。

提示:

  • 1 <= len(A), len(B) <= 1000
  • 0 <= A[i], B[i] < 100

思路:
本题我们考虑用一个二维数组记录两个字符串,这样就能轻松比较二者重复字母

1.确定dp数组及其下标含义:dp[i][j] :以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组长度为dp[i][j]。 (特别注意: “以下标i - 1为结尾的A” 标明一定是 以A[i-1]为结尾的字符串 ),注意这里这么写了就表示dp数组就一定是从dp[1][1]开始的

2.确定递推公式:

根据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;

3.dp数组的初始化:按照dp数组的定义来讲,dp[0][0]其实没有意义,你总不能是以下标-1为结尾的A数组吧。但是为了遍历能正常进行,我们还是把dp[0][0]设置成0,只有这样dp[1][1]才能等于dp[0][0] + 1,后续的dp数组也才能够递增下去

4.确定遍历顺序:本题外层放a还是b其实无所谓,最重要的确保双层循环即可,而且从前往后遍历

5.举例推导dp数组

拿示例1中,A: [1,2,3,2,1],B: [3,2,1,4,7]为例,画一个dp数组的状态变化,如下:

代码示例:

function findLength(nums1: number[], nums2: number[]): number { const length1: number = nums1.length const length2: number = nums2.length const dp: number[][] = new Array(length1 + 1).fill(0).map(_ => new Array(length2 + 1).fill(0)) let res: number = 0 for (let i = 1; i <= length1; i++) { for (let j = 1; j <= length2; j++) { if (nums1[i - 1] === nums2[j - 1]) { dp[i][j] = dp[i - 1][j - 1] + 1 res = Math.max(dp[i][j], res) } } } return res };
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/22 13:23:20

Linux平台设备驱动

Linux内核使用总线来处理设备&#xff0c;总线连接了CPU与这些设备。有些总线足够智能&#xff0c;并内嵌了可发现性逻辑以枚举连接到总线上的设备。在引导阶段的初期&#xff0c;Linux内核会请求这些总线提供它们所枚举的设备以及这些设备正常工作所需的资源&#xff08;如中断…

作者头像 李华
网站建设 2026/4/26 5:53:01

初探 Mysql Docker

前言我在專案開發階段常使用 MySQL Docker&#xff0c;主要是好處&#xff0c;快速啟動、零安裝成本、版本切換&#xff0c;不污染本機系統(不喜歡本機裝一堆有的沒的&#xff0c;特別是有的只會用那麼一次)。而且如果在需要&#xff0c;任何時間、任何機器&#xff0c;都能還原…

作者头像 李华
网站建设 2026/4/30 13:52:13

大数据传输时代:如何选择高效可靠的数据传输工具?

在数据驱动决策的今天&#xff0c;大数据已成为企业核心资产。然而&#xff0c;随着数据量呈几何级数增长&#xff0c;海量数据的快速、安全、稳定迁移与同步&#xff0c;正成为众多企业数字化转型道路上的严峻挑战。传统的传输方式在TB甚至PB级的数据洪流面前&#xff0c;如同…

作者头像 李华
网站建设 2026/4/21 18:28:40

企业网盘私有化部署,构建安全高效的数据资产管理基石

在数字化转型浪潮中&#xff0c;企业数据资产的价值与日俱增&#xff0c;如何安全、高效地存储、管理与协作这些核心资产&#xff0c;成为每个组织必须面对的关键议题。近年来&#xff0c;越来越多的企业将目光投向网盘系统的私有化部署&#xff0c;这一模式正逐渐成为保障数据…

作者头像 李华
网站建设 2026/4/20 22:17:58

39、深入探究 Linux 中的睡眠与计时机制

深入探究 Linux 中的睡眠与计时机制 在 Linux 系统编程中,睡眠和计时是常见的操作,它们在很多场景下都发挥着重要作用。本文将详细介绍 Linux 中不同的睡眠和计时接口,包括它们的特点、使用方法以及适用场景。 1. 纳秒级睡眠:nanosleep() Linux 中, usleep() 函数已被…

作者头像 李华
网站建设 2026/4/28 21:26:50

31、Linux 动态内存管理全解析

Linux 动态内存管理全解析 1. 映射文件与内存管理接口概述 大多数地址空间包含少量映射文件,如程序可执行文件本身、C 语言及其他共享库和数据文件。可以查看 /proc/self/maps 或 pmap 程序的输出,了解进程中的映射文件示例。Linux 提供了一系列接口用于获取和释放内存…

作者头像 李华