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)