导语
leetcode刷题笔记记录,主要记录题目包括:
Leetcode 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-104 <= nums[i] <= 104
进阶:
- 你能将算法的时间复杂度降低到
O(n log(n))吗?
解法
于每一个新元素nums[i],我们遍历其之前所有的元素,看是否能找到一个递增的子序列,使得我们可以把nums[i]添加到该子序列的末尾。使用动规五部曲:
- dp[i]表示以
nums[i]结尾的最长递增子序列(LIS)的长度。 - 递推公式:循环遍历所有i之前的j,若nums[i]>nums[j],则取;
- 初始化:dp[i]=1
- 遍历顺序:外层循环从前向后,内层循环无所谓
- 打印dp数组:略
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
# 初始化 dp 数组,所有位置都设为 1,因为单个元素自身就是一个长度为 1 的递增子序列
dp = [1] * len(nums)
# 从第二个元素开始遍历整个数组
for i in range(1, len(nums)):
# 对于每一个元素,查找在其之前的所有元素
for j in range(i):
# 如果当前元素(nums[i])大于其之前的某个元素(nums[j])
if nums[i] > nums[j]:
# 更新 dp[i],这里是寻找以 nums[i] 结尾的最长递增子序列
dp[i] = max(dp[j] + 1, dp[i])
# 返回最长递增子序列的长度,即 dp 数组中的最大值
return max(dp)
Leetcode 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。
提示:
1 <= nums.length <= 104-109 <= nums[i] <= 109
解法
使用 dp 数组,其中 dp[i] 存储的是以 nums[i] 结尾的最长连续递增子序列的长度。由于这是一个连续递增的序列,因此只需要比较当前元素与前一个元素的大小关系,然后决定是否更新 dp[i]。
class Solution:
def findLengthOfLCIS(self, nums: List[int]) -> int:
# 初始化 dp 数组,其中每个元素都被设置为 1。
# dp[i] 表示以 nums[i] 结尾的最长连续递增子序列的长度。
dp = [1] * len(nums)
# 从第二个元素开始遍历数组。
for i in range(1, len(nums)):
# 如果当前元素比前一个元素大,则连续递增子序列长度加 1。
if nums[i] > nums[i-1]:
dp[i] = dp[i-1] + 1 # 使用前一个状态来更新当前状态
# 返回最长连续递增子序列的长度
return max(dp)
Leetcode 718. 最长重复子数组
题目描述
给两个整数数组 nums1 和 nums2 ,返回 两个数组中 公共的 、长度最长的子数组的长度 。
示例 1:
输入: nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7]
输出: 3
解释: 长度最长的公共子数组是 [3,2,1] 。
示例 2:
输入: nums1 = [0,0,0,0,0], nums2 = [0,0,0,0,0]
输出: 5
提示:
1 <= nums1.length, nums2.length <= 10000 <= nums1[i], nums2[i] <= 100
解法
- dp数组含义:以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组长度为dp[i][j]。 (特别注意: “以下标i - 1为结尾的A” 标明一定是 以A[i-1]为结尾的字符串 )
- 递推公式:当A[i - 1] 和B[j - 1]相等的时候,dp[i][j] = dp[i - 1][j - 1] + 1;根据递推公式可以看出,遍历i 和 j 要从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。同时题目要求长度最长的子数组的长度。所以在遍历的时候顺便把dp[i][j]的最大值记录下来。
- 打印dp数组
完整版代码如下:
class Solution:
def findLength(self, nums1: List[int], nums2: List[int]) -> int:
# m 和 n 是 nums1 和 nums2 的长度加 1,方便在 dp 数组中使用
m, n = len(nums1) + 1, len(nums2) + 1
# 初始化一个 m * n 的二维数组,所有元素都为 0
dp = [[0] * n for _ in range(m)]
# 初始化一个变量,用来存储最长公共子数组的长度
max_length = 0
# 从 1 开始遍历,因为 dp 数组的第一行和第一列都是 0,已经初始化
for i in range(1, m):
for j in range(1, n):
# 如果 nums1 和 nums2 在 i-1 和 j-1 的位置上的元素相等
if nums1[i - 1] == nums2[j - 1]:
# 更新 dp[i][j],它是左上角的 dp[i-1][j-1] 加上 1
dp[i][j] = dp[i - 1][j - 1] + 1
# 更新最大长度
max_length = max(dp[i][j], max_length)
# 返回最大长度
return max_length