代码随想录27期|Python|Day50|​动态规划|​1143. 最长公共子序列|1035. 不相交的线|53. 最大子数组和|392.判断子序列

1143. 最长公共子序列

本题相对于连续最长公共子序列而言,本题不要考虑连续问题,不同的是更新公式的不同。

之前一题,连续递推更新公式:

                if nums1[i - 1] == nums2[j - 1]:
                    # 在当前位置上的最长公共子数组长度为前一个位置上的长度加一
                    dp[i][j] = dp[i - 1][j - 1] + 1
                # 更新最长公共子数组的长度
                if dp[i][j] > result:
                    result = dp[i][j]

可以看出,dp[i][j]的状态只能由前一个来推导出。也就是更新公式是唯一的,并且需要伴随的记录来保证存到最大值(因为不一定最后一个保存的是最长的)。

本题,即不需连续的公式:

                if text1[i-1] == text2[j-1]:
                    dp[i][j] = dp[i-1][j-1] + 1
                else:
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1])

可以看出,在原来的基础上加上了一个else的操作,也就是,如果当前的dp[i][j]在当前数字上不能被增加的话,就取上一个状态的最大值

上一个状态就是分别在两个字符串中前一个位置的两个状态max(dp[i][j-1], dp[i-1][j]),一个异步的操作。

1、确定dp数组的含义

dp[i][j]表示在截止到text1和text2的前i-1和j-1的最大公共子序列的长度。

2、确定更新公式

按照上面的分析,更新公式可以分为:

(1)如果当前的i-1和j-1位置的字符相等,那么更新:dp[i][j] = dp[i-1][j-1] + 1

(2)当前i-1和j-1的字符不相等,那么就取最大值:dp[i][j] = max(dp[i-1][j], dp[i][j-1])

3、确定初始化

在之前最大子串的初始化时,基于dp数组的含义,dp[0][0]是表示在没有开始匹配的时候最大的子串长度,所以全部是0。

4、确定遍历顺序

遍历顺序需要根据更新顺序来确定,因此应该是两个for循环内都是从小到大的顺序。

class Solution(object):
    def longestCommonSubsequence(self, text1, text2):
        """
        :type text1: str
        :type text2: str
        :rtype: int
        """

        dp = [[0] * (len(text2) + 1) for _ in range(len(text1) + 1)]

        for i in range(1, len(text1) + 1):
            for j in range(1, len(text2) + 1):
                if text1[i-1] == text2[j-1]:
                    dp[i][j] = dp[i-1][j-1] + 1
                else:  # 如果不匹配,则从之前两个状态中选最大的长度
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1])

        return dp[-1][-1]

说明

关于最后返回值的大小,本题区分于之前的题目在于不需要创建一个伴随值来记录长度,因为不连续的子串长度可以一直保存到最后。所以返回值是dp[-1][-1]

1035. 不相交的线

对于两个不想交的直线,也是就是说nums1在nums2对应的点必须顺序一致,也就是顺序(不连续)的子序列

这个公共子序列指的是相对顺序不变(即数字4在字符串A中数字1的后面,那么数字4也应该在字符串B数字1的后面)。

所以和上一题是完全一样的思路和题解。就不过多解释了。

class Solution(object):
    def maxUncrossedLines(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: int
        """
        dp = [[0] * (len(nums2) + 1) for _ in range(len(nums1) + 1)]

        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
                else:
                    dp[i][j] = max(dp[i][j-1], dp[i-1][j])
            
        return dp[-1][-1]

53. 最大子数组和

本题需要在连续的基础上做一个买股票的动态规划。

1、dp数组的含义

dp[i]一维数组,表示当前的位置i处,所存在的最大子串和,注意是必须包含当前i索引的数字;

2、dp数组的更新

(1)加上当前nus[i]数字后,比前一个dp[i-1]大,则当前dp值为前一个dp加上当前数字

(2)加上当前数字之后比之前的dp值小,则从当前数字开始从头计算

3、dp数组的初始化

由于dp的更新只跟前一个dp有关系,所以dp的初始化就是dp[0] = nums[0]。

4、确定遍历顺序

遍历顺序是从小到大即可。

class Solution(object):
    def maxSubArray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if len(nums) == 0:
            return 0
        dp = [0] * len(nums)
        dp[0], res = nums[0], nums[0]
        for i in range(1, len(nums)):
            dp[i] = max(dp[i-1] + nums[i], nums[i])
            res = max(res, dp[i])
        return res

392. 判断子序列 

判断是否是子序列相当于是判断是否包含相同子序列的操作中,要求短字符串(s)进行全匹配,也就是不进行删除,最后判断dp数组最后一个值是否和s的长度相等,如果相等,输出true,如果不相等,则输出false。

因此,本题的关键就是在更新dp的时候如何保持s中的index不变,只改变t中的index进行更新。

1、dp数组的含义

dp数组的含义其实和之前的最长子串的含义一致,都是在i-1,j-1处的最长公共子序列长度。为什么不是i,j,是因为初始化的方便以及dp更新的时候依赖i-1和j-1的dp值。

2、确定dp更新公式

更新公式依然是根据当前i-1和j-1所处位置的字符是否一致来确定。

(1)如果当前的字符相等,则dp[i][j] = dp[i-1][j-1] + 1

(2)如果当前字符不相等,则更新为上一个dp值

注意,这里的更新为上一个dp值而不是max是因为s不能进行退位(index退回到i-1),是因为s需要进行全部的匹配,因此当前的i不能被跳过或者是进行删除,而必须被“保留”或者hold住。同时更改t序列中j的值进行回退。所以,dp[i][j] = dp[i][j-1]。

3、确定初始化

最长公共子序列的初始化一致,都是初始化为0。

4、确定遍历顺序

和最长公共子序列一样,i,j都是从小到大进行遍历。

class Solution(object):
    def isSubsequence(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: bool
        """
        dp = [[0] * (len(t) + 1) for _ in range(len(s) + 1)]

        for i in range(1, len(s) + 1):
            for j in range(1, len(t) + 1):
                if s[i-1] == t[j-1]:
                    dp[i][j] = dp[i-1][j-1] + 1
                else:
                    dp[i][j] = dp[i][j-1]  # 这里不需要max(dp[i-1][j]是因为必须要s全部参与匹配,不能跳过(删除)一些元素)
        return dp[-1][-1] == len(s)

 Day50完结!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值