代码随想录训练营第53天 | 1143.最长公共子序列 ● 1035.不相交的线 ● 53. 最大子序和

1143.最长公共子序列

题目链接:https://leetcode.com/problems/longest-common-subsequence/

解法:

1. 确定dp数组(dp table)以及下标的含义

dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp[i][j]

2. 确定递推公式

主要就是两大情况: text1[i - 1] 与 text2[j - 1]相同,text1[i - 1] 与 text2[j - 1]不相同

如果text1[i - 1] 与 text2[j - 1]相同,那么找到了一个公共元素,所以dp[i][j] = dp[i - 1][j - 1] + 1;

如果text1[i - 1] 与 text2[j - 1]不相同,那就看看text1[0, i - 2]与text2[0, j - 1]的最长公共子序列 和 text1[0, i - 1]与text2[0, j - 2]的最长公共子序列,取最大的。

即:dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]).

text1[i - 1] 与 text2[j - 1]相同的情况的递推公式,和最长重复子序列一样。但是最长重复子序列在不同时,不需要处理,原因是重新子序列需要是连续的,如果不同,那么那么当下的长度重新变为0了,就是初始值。

3. 返回值

dp[-1][-1]一定是最大值,因为不需要连续,而最长重复子序列中不一定的,所以需要不断更新result为最大值。

边界条件:无

时间复杂度:O(nm)

空间复杂度:O(nm)

class Solution(object):
    def longestCommonSubsequence(self, text1, text2):
        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]

1035.不相交的线   

题目链接:https://leetcode.com/problems/uncrossed-lines/

解法:

这道题其实和求最长公共子序列是一模一样的...

边界条件:

时间复杂度:

空间复杂度:

class Solution(object):
    def maxUncrossedLines(self, nums1, nums2):
        dp = [[0] * (len(nums2)+1) for i 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-1][j], dp[i][j-1])
        return dp[-1][-1]

53. 最大子序和

题目链接:https://leetcode.com/problems/maximum-subarray/

解法:

1. 确定dp数组(dp table)以及下标的含义

dp[i]:包括下标i(以nums[i]为结尾)的最大连续子序列和为dp[i]

2. 确定递推公式

dp[i]只有两个方向可以推出来:

  • dp[i - 1] + nums[i],即:nums[i]加入当前连续子序列和
  • nums[i],即:从头开始计算当前连续子序列和

一定是取最大的,所以dp[i] = max(dp[i - 1] + nums[i], nums[i]);

3. dp数组如何初始化

从递推公式可以看出来dp[i]是依赖于dp[i - 1]的状态,dp[0]就是递推公式的基础。

根据dp[i]的定义,很明显dp[0]应为nums[0]即dp[0] = nums[0]。

边界条件:无

时间复杂度:O(n)

空间复杂度:O(n)

# 贪心版本
class Solution(object):
    def maxSubArray(self, nums):
        
        result = float("-inf")
        count = 0
        for n in nums:
            count += n

            # 这个判断得放在对count更新之前
            if count > result:
                result = count

            # 如果子序列和为负了,就把下一个元素作为起点
            if count <= 0:
                count = 0
            
        return result
# 动态规划
class Solution(object):
    def maxSubArray(self, nums):
    # 这里必须强调是以nums[i]结尾的最大和,
    # 而不是截止到i的最大和(可能以i-1结尾)
        dp = [0] * len(nums)
        dp[0] = nums[0]
        for i in range(1, len(nums)):
            # dp的每个元素是以nums[i]结尾的最大和,那么只有两个情况
            # 一种是dp[i-1]为负,那么直接取nums[i],不管nums[i]是正还是负
            # 一种是dp[i-1]为正,那么就是dp[i-1]+nums[i],因为i位置加了正的才变大
            dp[i] = max(nums[i], dp[i-1]+nums[i])
        return max(dp)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值