代码随想录27期|Python|Day49|动态规划| 300. 最长递增子序列|674. 最长连续递增序列|718. 最长重复子数组

 300. 最长递增子序列

本题是子序列一套的开始。

1、确定dp数组的含义

本题中,正确定义dp数组的含义十分重要。

dp[i]表示i之前包括i的以nums[i]结尾的最长递增子序列的长度。

2、确定初始化

每一个数字都可以独立构成一个子序列,所以数组初始化全部为1.

3、确定递推公式

在本题的遍历过程中,由于序列构成子序列是不连续删除构成的,所以递推公式不能确定为由之前某一个状态直接推到而来,所以在递推的公式中,需要进行遍历,再遍历中不断保存最优解。

if nums[i] > nums[j]: # 如果当前数字和i可以构成子序列

        dp[i] = max(dp[i], dp[j] + 1) # 更新i的最大长度

4、确定遍历顺序

外层遍历:遍历序列,i控制,从小到大

内层遍历:遍历0到i-1的子序列,j控制,从小到大

class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        dp = [1] * len(nums)  # 全部初始化为1,因为最小长度就是1

        for i in range(1, len(nums)):
            for j in range(0, i):  # 从0开始找i内部的全部子序列
                if nums[i] > nums[j]:  # 如果当前数字和i可以构成子序列
                    dp[i] = max(dp[i], dp[j] + 1)  # 更新i的最大长度
        return max(dp)

674. 最长连续递增序列

本题在上一题的基础上进行了限制:只能连续才能更新dp。

实际上就是在dp更新上做了变化:

if nums[i] > nums[i-1]:

dp[i] = max(dp[i], dp[i-1] + 1) # 这里只和前一个数字比较,所以不需要嵌套for循环

由于不需要考虑不连续的形式,所以dp的更新只要和之前一个数字的dp + 1比就可以了。

class Solution(object):
    def findLengthOfLCIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        dp = [1] * len(nums)
        for i in range(1, len(nums)):
            if nums[i] > nums[i-1]:
                dp[i] = max(dp[i], dp[i-1] + 1)  # 这里只和前一个数字比较,所以不需要嵌套for循环
        return max(dp)

718. 最长重复子数组

本题在第一题的基础上加上了一个匹配的环节。

1、dp数组的定义

关于本题,其实需要定义一个二维数组,因为需要在遍历一个数组的同时去遍历另外一个数组。

基于初始化的考虑,在更新的时候需要调用前一次,而直接初始化数组第一个位置的dp数组比较麻烦,相当于需要先来遍历一遍了。所以基于此,在dp数组的定义上稍加改动。

dp[i][j]表示以nums1的i-1和nums2的j-1为终止位置的最大重复子数组的长度。

这样在初始化的时候较为方便。

2、递推公式

很简单,把递增的判定条件改为==即可。

如果当前的条件满足,则当前的dp为上一个位置的dp+1。

为什么i、j是同步为i-1、j-1?

因为在匹配的时候,两个位置是共进退的(因为需要连续匹配,不是间隔)。

3、初始化

由于dp的定义整体前移,所以在初始化的时候需要在行列数量上各加一。然后全部置0。

4、遍历顺序

两层循环都是从小到大而且i和j是等价的,可以互换内外。

class Solution(object):
    def findLength(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: int
        """
        dp = [[0] * (len(nums2) + 1) for _ in range(len(nums1) + 1)]
        res = 0  # res来保存每一个最大的可能值
        for i in range(1, len(nums1)+1):
            for j in range(1, len(nums2)+1):
                if nums1[i-1] == nums2[j-1]:
                    dp[i][j] = dp[i-1][j-1]+1
                res = max(dp[i][j], res)  # 更新res
        return res

这里还想说一下关于输出。

因为dp下标的含义,所以不一定是保证取到最后一个数字的时候就恰好是最优解,需要在过程中不断去更细全局最优解(因为可能出现在数组中间位置)。

所以最后的输出不是return max(dp[-1]),而是另外储存每一个最大值

Day49完结!!!

这里有一个想法

二刷之前希望先学会时间和空间复杂度的计算和比较,卡玛网上面每一个解法都有对应的时空复杂度,这样二刷的时候相当于把这块练手了。

你可能感兴趣的:(算法,python)