LeetCode 300. 最长递增子序列 / 673. 最长递增子序列个数(Medium)/ 子数组、序列问题!!!

  • 128. 最长连续序列(Medium)
  • 674. 最长连续递增序列(Easy)
  • 581. 最短无序连续子数组(Medium)
  • 最长公共子串 / 最长公共子序列
  • 1092. 最短公共超序列(Hard)
  • 718. 最长(公共)重复子数组(Medium)
  • 剑指Offer 42. 连续子数组的最大和(Easy)/ 918. 环形子数组的最大和(Medium)
  • 未排序数组中累加和为给定值 target 的最长子数组长度
  • 209. 满足target的长度最小的子数组(Medium)
    LeetCode 300. 最长递增子序列 / 673. 最长递增子序列个数(Medium)/ 子数组、序列问题!!!_第1张图片
    【题目链接】

题解

  1. 最长上升子序列(动态规划 + 二分查找,清晰图解)

思路

LeetCode 300. 最长递增子序列 / 673. 最长递增子序列个数(Medium)/ 子数组、序列问题!!!_第2张图片
LeetCode 300. 最长递增子序列 / 673. 最长递增子序列个数(Medium)/ 子数组、序列问题!!!_第3张图片
LeetCode 300. 最长递增子序列 / 673. 最长递增子序列个数(Medium)/ 子数组、序列问题!!!_第4张图片

代码

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

LeetCode 300. 最长递增子序列 / 673. 最长递增子序列个数(Medium)/ 子数组、序列问题!!!_第5张图片

最长递增子序列的个数

LeetCode 300. 最长递增子序列 / 673. 最长递增子序列个数(Medium)/ 子数组、序列问题!!!_第6张图片

  • 题解
    LeetCode 300. 最长递增子序列 / 673. 最长递增子序列个数(Medium)/ 子数组、序列问题!!!_第7张图片
    LeetCode 300. 最长递增子序列 / 673. 最长递增子序列个数(Medium)/ 子数组、序列问题!!!_第8张图片
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) # 统计不同的最长的递增子序列的总个数

你可能感兴趣的:(LeetCode,动态规划,二分法)