第三天坚持,加油啊!
300. 最长递增子序列
思路:
思路: 最长递增子序列,dp[i]表示以nums[i]为结尾的最长递增子序列的长度。
初始化:1,每个元素最短是以自身为子序列,长度为1
递推:对于每一个元素dp[i],需要找到j∈[0,...,i-1]中nums[j] < nums[i]的dp[j],然后取max(dp[i], dp[j]+1)
递推方向:从左往右
最终答案是dp中最大的
class Solution(object):
def lengthOfLIS(self, nums):
n = len(nums)
# if n == 1: return 1
# dp初始化
dp = [1] * n
res = 0
for i in range(n): # 注意,如果1-n,需要判断n=1的base case返回1
for j in range(i):
if nums[i] > nums[j]:
dp[i] = max(dp[i], dp[j]+1)
res = max(res, dp[i])
return res
674. 最长连续递增序列
此题需要连续的递增子序列最大长度,不能删除元素。
思路:
想上去和第一题类似,之前是通过和0~i-1的dp[j]比较,完成递增子序列。如果要连续的子序列,那是不是最多和前面一次比较。
class Solution:
def findLengthOfLCIS(self, nums: List[int]) -> int:
n = len(nums)
if n == 1: return 1
dp = [1] * n
res = 0
for i in range(1, n):
if nums[i-1] < nums[i]:
# dp[i] = max(dp[i], dp[i-1]+1)
dp[i] = dp[i-1] + 1
res = max(res, dp[i])
return res
718. 最长重复子数组
需要找到两个数组中公共的最长子数组,子数组区别于子序列,子数组是必须要连续的。
思路:
本题是二维的dp。
定义:dp[i][j]表示nums1[0,..,i]和nums[0,..,j]的最长公共子数组,最终返回dp[m][n], dp:(m+1)*(n+1)
初始化:一边是空数组时候,公共长度为0
~~递推:尝试和左边、上边,左上角看看有没有递推关系,~~
看了代码随香录的思路:
和最长公共子序列LCS类似,需要二维的dp来递推,并且子数组是连续的,dp[i][j]只能通过dp[i-1][j-1]递推得到
定义:dp[i][j]表示s1[0:i-1]和s2[0:j-1]两个子数组的最大公共子数组长度,其中两个子数组长度分别是i和j,例如dp[2][4]=2表示ad adec两个子数组的最大公共子数组长度
初始化:任意一边空数组时候,dp=0
递推:子数组问题是连续的,只和前一个有关,当nums[i-1]和nums2[i-1]相等时候,dp[i][j] = dp[i-1][j-1] + 1
递推方向:左上到右下,先迭代nums1再迭代nums2
class Solution:
def findLength(self, nums1: List[int], nums2: List[int]) -> int:
m, n = len(nums1), len(nums2)
dp = [[0] * (n+1) for _ in range(m+1)]
res = 0
for i in range(1, m+1):
for j in range(1, n+1):
# dp[i][j]是s1[0...i-1] s2[0...j-1],如果末尾相等,那就可以从dp[i-1][j-1]推来
if nums1[i-1] == nums2[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
res = max(res, dp[i][j]) # 注意在内层循环更新!
return res
1143. 最长公共子序列
思路:
定义:dp[i][j]表示s1[0..i-1]和s2[0..j-1]的最长公共子序列长度,
初始化:
递推:如果最后的s1[i-1]==s2[j-1],那么dp[i][j] = dp[i-1][j-1]+1;如果不相同,取max(dp[i-1][j], dp[i][j-1])
class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
m, n = len(text1), len(text2)
dp = [[0] * (n + 1) for _ in range(m+1)]
res = 0
for i in range(1, m+1):
for j in range(1, n+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])
res = max(res, dp[i][j])
return res
1035. 不相交的线
思路:
要找两个序列相同的数字,不相交就是顺序画,实际在做最长公共子序列。
class Solution:
def maxUncrossedLines(self, nums1: List[int], nums2: List[int]) -> int:
m, n = len(nums1), len(nums2)
dp = [[0] * (n+1) for _ in range(m+1)]
res = 0
for i in range(1, m+1):
for j in range(1, n+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])
res = max(res, dp[i][j])
return res
53. 最大子数组和
思路:
求连续的子数组的最大和。
定义:dp[i]表示以i结尾的子数组的最大和。
递推:dp[i] = max(nums[i], dp[i-1] + nums[i]) # 或者从当前元素开始,或者之前的加上现在的
初始化:dp[0] = nums[0]
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
if not nums:
return 0
n = len(nums)
# 初始化
dp = [0] * n
dp[0] = nums[0]
# res =0
res = dp[0] # 注意,res不能取0!
for i in range(1, n):
dp[i] = max(dp[i-1] + nums[i], nums[i])
res = max(res, dp[i])
return res
392. 判断子序列
思路1:双指针
双指针,迭代t,遇到和s相等的字符,s的指针也前进1
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
m, n = len(s), len(t)
i = j = 0
while i < m and j < n:
if s[i] == t[j]:
i += 1
j += 1
return i == m
思路2: 动态规划
动态规划,参考最长公共子序列,dp[i][j]表示s[0..i-1]和t[0..j-1]的最长公共子序列。如果s[i-1]==t[j-1],那么长度+1;如果不相等,由于t>s,那么需要t-1,即j-2,也就是dp[i][j] = dp[i][j-1]
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
m, n = len(s) , len(t)
dp = [[0] * (n+1) for _ in range(m+1)]
for i in range(1, m+1):
for j in range(1, n+1):
if s[i-1] == t[j-1]:
dp[i][j] = dp[i-1][j-1] + 1 # 出现公共字符,在dp[i-1][j-1]基础上+1
else:
dp[i][j] = dp[i][j-1] # 不相同,需要从t中删除字符
return dp[m][n] == m # 如果最长公共子序列长度为m就返回true