- 子序列
- 子数组(连续的)
- 674. 最长【连续】递增子数组(Easy)
- 581. 【找出并升序】最短无序连续子数组(Medium)/ 面试题 16.16. 部分排序
- 718. 【2 个数组的】最长公共子数组【最长】
- 剑指Offer 42. 连续子数组的最大和(Easy)/ 918. 环形子数组的最大和(Medium)
- 无序数组中和为 S 的最长子数组【最长】
- 209. 总和【大于等于】S 的【最短】子数组(Medium)/ 523.(总和为 K 的倍数 + 长度至少为 2 的)连续子数组(M)
- 689. 三个无重叠子数组的最大和(Hard)/ 1031. 两个【非重叠】子数组的最大和(M)
- 152. 乘积最大的子数组(Medium)
300. 最长递增子序列
class Solution:
### 1215 动态规划(3656 ms,15 MB)
def lengthOfLIS(self, nums: List[int]) -> int:
if not nums: return 0
# 初始化dp,1表示每个数字都以自身为最小的有序子数组
dp = [1] * len(nums)
# 遍历原数组nums中的每一个数num
for i in range(len(nums)):
# 遍历在num之前的每一个数
for j in range(i):
# 若当前两个数是有序关系,则更新dp[i];否则表示有序关系中断,则已有的值会更大,此时无需更新
if nums[i] > nums[j]: dp[i] = max(dp[i], dp[j]+1)
return max(dp) # 最后返回dp的最大值
### 1215 动态规划 + 二分法(68 ms,15 MB)
def lengthOfLIS(self, nums: List[int]) -> int:
# 初始化单调递增(子)数组dp,即dp的有效长度dp_len
# dp[i]表示长度为i+1的子序列的最后一个元素的值,dp是单调递增的
dp, dp_len = [0] * len(nums), 0
# 遍历原数组nums中的每一个数num
for num in nums:
i, j = 0, dp_len
# 二分查找,为当前num寻找到dp中合适的位置
# 检查dp[0:dp_len]中是否存在比num大的值,若存在则将num插入对应的位置
# 保证整个dp是递增的
while i < j:
m = (i + j) // 2
# 未找到大于num的数时,i前移;否则j前移(<表示严格单调,<=表示非严格单调)
if dp[m] < num: i = m + 1
else: j = m
# 更新dp[i]
dp[i] = num
# 若当前值num大于dp中的最后一个值(即dp的最大值,此时下标j未改变)
# 则dp的有效长度dp_len加一(即表示num加入后可以构成更长的有序子数组)
if j == dp_len: dp_len += 1
return dp_len
- 返回最长递增子序列
def LIS(nums):
if len(nums) == 0: return []
if len(nums) == 1: return nums
n = len(nums)
# a[i]表示以下标为i的元素作为结尾的最长递增子序列的长度
a = [0] * n
# dp[i]表示长度为i+1的子序列的最后一个元素的值,dp是单调递增的
dp = [0] * n
dp_len = 0 # 最长递增子序列的长度,从0开始增加到最后的值
for i in range(n):
# 二分查找,为当前num寻找到dp中合适的位置
# 检查dp[0:dp_len]中是否存在比nums[i]大的值,若存在则将nums[i]插入对应的位置
# 保证整个dp是递增的
l, r = 0, dp_len
while l < r:
mid = (l + r) // 2
if dp[mid] < nums[i]:
l = mid + 1
else:
r = mid
# 每过一个数nums[i],则把它作为长度为l+1的子序列的最后一个元素,是一种更新操作
dp[l] = nums[i]
# 若当前值num大于dp中的最后一个值(即dp的最大值,此时下标j未改变)
# 则dp的有效长度dp_len加一(即表示num加入后可以构成更长的有序子数组)
if r == dp_len: dp_len += 1
a[i] = r + 1 # 每过一个数都能确定以该数结尾的最长递增子序列的长度
# 根据dp_len的长度递减,来从后往前寻找最长递增子序列中的每一个数
res = [0] * dp_len
for i in range(n - 1, -1, -1):
# 若当前的dp_len恰好为a中的某一个值,则寻找到其中的一个数
if dp_len == a[i]:
# 根据dp_len作为下标,把元素放入对应的位置,然后dp_len长度减一
res[dp_len - 1] = nums[i]
dp_len -= 1
return res
673. 最长递增子序列个数
class Solution(object):
def findNumberOfLIS(self, nums):
n = len(nums)
if n <= 1: return n
dp = [0] * n # dp[i]表示以nums[i]结尾的最长递增子序列的长度
counts = [1] * n # counts[i]表示以nums[i]结尾的最长递增子序列的个数
for j, num in enumerate(nums):
for i in range(j):
if nums[i] < nums[j]: # 若当前数i < 固定数j,才表明出现更长的子序列
if dp[i] >= dp[j]: # 若是初始化的值,则最长子序列的长度直接在dp[i]的基础上加一
dp[j] = dp[i] + 1
counts[j] = counts[i] # 若是初始化的值,则最长子序列的个数与counts[i]相同
elif dp[i] + 1 == dp[j]: # 若j之前某一个值i能与j构成递增子序列,则j处的最长子序列的个数会累加上i处的个数
counts[j] += counts[i]
longest = max(dp)
return sum(c for i, c in enumerate(counts) if dp[i] == longest) # 统计不同的最长的递增子序列的总个数