300
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
dp=[1]*(len(nums))
if len(nums)<=1:
return len(nums)
res=0
for j in range(1,len(nums)):#因为i要比j小,所以j应该从1开始。
for i in range(j):
if nums[j]>nums[i]:
dp[j]=max(dp[j],dp[i]+1)#不是为了比较两者大小,而是要取dp[i]+1的最大值。
if dp[j]>res:res=dp[j]#最长子序列并不一定出现在dp的最后一个。所以我们要判断记录一下,最终
#选取max(dp)。
return res
'''分析过程:
1、dp含义:代表当下标为j的时候,最长子序列长度
2、递推公式。首先我们判断的是最长子序列,所以我们要明确,当nums[j]>nums[i]的时候,说明dp[j]应该更新了,
但是如果当前的dp[j]如果≥dp[i]+1,就不需要更新,否则更新成为dp[i]+1。这样才能保证dp[j]长度最长。
3、初始化,因为规定了nums非空,所以无论如何,最长子序列长度最少为1。
4、遍历顺序,由于dp[j]依赖于dp[i],所以从前往后。
'''
1143
class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
len1=len(text1)
len2=len(text2)
dp=[[0 for _ in range(len2+1)] for _ in range(len1+1)]
for i in range(1,len1+1):
for j in range(1,len2+1):
if text1[i-1]==text2[j-1]:
dp[i][j]=dp[i-1][j-1]+1#这一步和718题一样,如果相等就从左上向右下+1.
else:
dp[i][j]=max(dp[i-1][j],dp[i][j-1])
return dp[-1][-1]
'''else这里这里我们要处理让该题不连续也可以继续统计最长。#如果两者不相等的话,那么肯定取之前的某个最大的
公共子序列,那么这个最大子序列可能有两个来源,要么是从其左边来,要么就是从其上方来,取最大者。'''
1035
class Solution:
def maxUncrossedLines(self, nums1: List[int], nums2: List[int]) -> int:
len1=len(nums1)
len2=len(nums2)
dp=[[0 for _ in range(len2+1)] for _ in range(len1+1)]
for i in range(1,len1+1):
for j in range(1,len2+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]
'''和1143题一模一样,这题的意思就是,在两个数组中,只要数字的相对顺序不变,那么就有几个不相交的直线。'''
583
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
len1 = len(word1)
len2 = len(word2)
dp = [[0 for _ in range(len2 + 1)] for _ in range(len1 + 1)]
for i in range(1, len1 + 1):
for j in range(1, len2 + 1):
if word1[i - 1] == word2[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 len1+len2-2*dp[-1][-1]
'''1143同类型的题,最小删除步数,其实就等于,两个字符串的长度和,减去2倍的最长子序列长度就ok了'''
392
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
lens=len(s)
lent=len(t)
dp=[[0 for _ in range(lent+1)] for _ in range(lens+1)]
for i in range(1,lens+1):
for j in range(1,lent+1):
if s[i-1]==t[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]==lens
'''只需要判断两个字符串的最长公共子序列长度是不是和s一样就可以了。如果不一样,一定不是子序列。'''
115
class Solution:
def numDistinct(self, s: str, t: str) -> int:
lens=len(s)
lent=len(t)
dp=[[0 for _ in range(lent+1)] for _ in range(lens+1)]
for i in range(lens+1):
dp[i][0]=1
for i in range(1,lens+1):
for j in range(1,lent+1):
if s[i-1]==t[j-1]:
dp[i][j]=dp[i-1][j-1]+dp[i-1][j]
else:
dp[i][j]=dp[i-1][j]
return dp[-1][-1]
'''1、dp代表t在s中出现的个数。
2、递推公式。和判断子序列有整体架构相似,但是递推公式不同,因为那是求是不是子序列,这是求子序列的个数。
为什么会有个数差异呢?是因为t的某些元素在s中并不是只有一个,我们需要选择是哪一个。这样问题就来了。
我们原来如果匹配到s[i-1]==t[j-1],说明当前s元素和t元素相同,我们可以选择他匹配,也可以不选择。
为什么可以不选择呢?因为t元素在s中不一定是唯一的。那么这就造成了,dp[i][j]的来源是两个,
一个是左上方,一个是正上方。
如果不相等,那么当前dp一定和它正上方的dp相同。
3、初始化,我们判断t是不是s的子序列。所以dp[0][j]说明s是空字符串,都应该为0.
dp[i][0]说明t是空字符串,肯定是s的子序列。所以初始化为1.dp[0][0]也应该为1.
4、遍历顺序。当前dp取决于左上方和正上方。正序遍历。'''
674
class Solution:
def findLengthOfLCIS(self, nums: List[int]) -> int:
n=len(nums)
if n<=1:
return n
dp=[1]*n
res=0
for i in range(1,n):
if nums[i]>nums[i-1]:
dp[i]=dp[i-1]+1
if dp[i]>res:res=dp[i]
return res
'''分析过程:
1、dp含义:代表当下标为j的时候,连续最长子序列长度
2、递推公式。首先我们判断的是最长子序列,因为是连续最长子序列所以我们要明确,dp[i]依赖于dp[i-1],当nums[i]>nums[i-1]的时候,说明dp[i]应该更新了,否则我们需要用res记录一下,让这次的结果和以后出现的dp[i]进行比较。这样才能保证dp[j]长度最长。
3、初始化,因为规定了nums非空,所以无论如何,最长子序列长度最少为1。
4、遍历顺序,由于dp[i]依赖于dp[i-1],所以从前往后。
'''
718
class Solution:
def findLength(self, nums1: List[int], nums2: List[int]) -> int:
dp=[[0 for _ in range(len(nums2)+1)] for _ in range(len(nums1)+1)]
res=0
for i in range(1,len(nums1)+1):
for j in range(1,len(nums2)+1):
if nums1[i-1]==nums2[j-1]:#最长重复子数组依赖于前一个是否相等。在dp矩阵中,就是体现在从左上向右下递增
dp[i][j]=dp[i-1][j-1]+1
if res<dp[i][j]:res=dp[i][j]#同样应该在全dp内找最大,而不是最后一位。所以要放在第二个for内。
return res