动态规划整理
1.最长公共子序列 leetcode300
# 给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列。
#
# 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的状况下删除某些字符(也能够不删除任何字符)后组成的新字符串。
# 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde"
# 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。
#
# 若这两个字符串没有公共子序列,则返回 0。
# 示例 1:
#
# 输入:text1 = "abcde", text2 = "ace"
# 输出:3
# 解释:最长公共子序列是 "ace",它的长度为 3。
class Solution(object):
def longestCommonSubsequence(self, text1, text2):
"""
:type text1: str
:type text2: str
:rtype: int
"""
m,n=len(text1),len(text2)
dp=[[0 for _ in range(m+1)] for _ in range(n+1)]
for i in range(1,n+1):
for j in range(1,m+1):
str1=text1[:j]
str2=text2[:i]
if str1[-1]==str2[-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]
2.最长上升子序列 leetcode300
# 给定一个无序的整数数组,找到其中最长上升子序列的长度。
#
# 示例:
#
# 输入: [10,9,2,5,3,7,101,18]
# 输出: 4
# 解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
#
# 说明:
# 可能会有多种最长上升子序列的组合,你只须要输出对应的长度便可。
# 你算法的时间复杂度应该为 O(n^2) 。
#
#
# 进阶: 你能将算法的时间复杂度下降到 O(n log n) 吗?
#
class Solution(object):
def lengthOfLIS(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if not nums:return 0
dp=[1]*len(nums) #dp[i]表示到i位置的最长上升子序列
for i in range(1,len(nums)):
for j in range(i):
if nums[i] > nums[j]: #每扫描到一个数,遍历在它以前的数字,选择比它小的
dp[i] = max(dp[i],dp[j] + 1) #状态转移
# 最后要所有走一遍,看最大值
return max(dp)
3.爬楼梯 leetcode70
# 假设你正在爬楼梯。须要 n 阶你才能到达楼顶。
#
# 每次你能够爬 1 或 2 个台阶。你有多少种不一样的方法能够爬到楼顶呢?
#
# 注意:给定 n 是一个正整数。
# 最核心的转移方程是res[i] = res[i-1] + res[i-2]
def climbStairs2(self, n):
if n == 1:
return 1
res = [0 for i in xrange(n)]
res[0], res[1] = 1, 2
for i in xrange(2, n):
res[i] = res[i-1] + res[i-2]
return res[-1]
4.最长公共子串
#给两个字符串,返回它们的最长公共子串的长度
#方法和最长公共子序列同样,不过最终造成的二维数组中子串应该连成一条斜线。
class Solution(object):
def LCS(self,str1,str2):
if not str1 or not str2:return 0
dp=[[0 for _ in range(len(str1))] for _ in range(len(str2))]
res=0
for j in range(len(str1)):
if str1[j]==str2[0]:
dp[0][j]=1
for i in range(len(str2)):
if str2[i]==str1[0]:
dp[i][0]=1
for i in range(1,len(str2)):
for j in range(1,len(str1)):
if str1[j]==str2[i]:
dp[i][j]=dp[i-1][j-1]+1
res=max(res,dp[i][j])
return res
4.单词拆分 leetcode139
# 给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,断定 s 是否能够被空格拆分为一个或多个在字典中出现的单词。
#
# 说明:
#
#
# 拆分时能够重复使用字典中的单词。
# 你能够假设字典中没有重复的单词。
#
#
# 示例 1:
#
# 输入: s = "leetcode", wordDict = ["leet", "code"]
# 输出: true
# 解释: 返回 true 由于 "leetcode" 能够被拆分红 "leet code"。
#
#
# 示例 2:
#
# 输入: s = "applepenapple", wordDict = ["apple", "pen"]
# 输出: true
# 解释: 返回 true 由于 "applepenapple" 能够被拆分红 "apple pen apple"。
# 注意你能够重复使用字典中的单词。
#
#
# 示例 3:
#
# 输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
# 输出: false
#
#
class Solution(object):
def wordBreak(self, s, wordDict):
"""
:type s: str
:type wordDict: List[str]
:rtype: bool
"""
n=len(s)
dp=[False]*(n+1)
dp[0]=True #这里的dp[0]=True并不表示位置,而是表示一种初始状态
for i in range(1,n+1):
for word in wordDict: #同一个位置可能有好几种到达的方式,因此要遍历wordDict
if i>=len(word) and dp[i-len(word)] and s[i-len(word):i]==word:
dp[i]=True #dp[i]为True表示从头分割的话能够分割到第i个位置来
return dp[-1]
5.不一样路径II leetcode63
# 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
#
# 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
#
# 如今考虑网格中有障碍物。那么从左上角到右下角将会有多少条不一样的路径?
#
#
#
# 网格中的障碍物和空位置分别用 1 和 0 来表示。
#
# 说明:m 和 n 的值均不超过 100。
#
# 示例 1:
#
# 输入:
# [
# [0,0,0],
# [0,1,0],
# [0,0,0]
# ]
# 输出: 2
# 解释:
# 3x3 网格的正中间有一个障碍物。
# 从左上角到右下角一共有 2 条不一样的路径:
# 1. 向右 -> 向右 -> 向下 -> 向下
# 2. 向下 -> 向下 -> 向右 -> 向右
#
#
#
class Solution(object):
def uniquePathsWithObstacles(self, obstacleGrid):
"""
:type obstacleGrid: List[List[int]]
:rtype: int
"""
if not obstacleGrid:return 0
row_len=len(obstacleGrid)
col_len=len(obstacleGrid[0])
obstacleGrid[0][0] =1-obstacleGrid[0][0]
for r in range(1,row_len): #初始化第一列,若是有1就把以后的都设为0,由于挡住了怎么也到不了
obstacleGrid[r][0]=obstacleGrid[r-1][0]*(obstacleGrid[r][0]==0)
for c in range(1,col_len): #初始化第一行
obstacleGrid[0][c]=obstacleGrid[0][c-1]*(obstacleGrid[0][c]==0)
for r in range(1,row_len):
for c in range(1,col_len): #到达一个位置的方法数=从上面到达的方法数+从左边到达的方法数
obstacleGrid[r][c]=(obstacleGrid[r-1][c]+obstacleGrid[r][c-1])*(obstacleGrid[r][c]==0)
return obstacleGrid[-1][-1]
6.三角形的最小路径
# 给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
#
# 例如,给定三角形:
#
# [
# [2],
# [3,4],
# [6,5,7],
# [4,1,8,3]
# ]
#
#
# 自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
#
# 说明:
#
# 若是你能够只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。
#
#
class Solution(object):
def minimumTotal(self, triangle):
"""
:type triangle: List[List[int]]
:rtype: int
"""
for i in range(1,len(triangle)):
for j in range(len(triangle[i])):
if j==0: #最左边
triangle[i][j]=triangle[i-1][j]+triangle[i][j]
elif j==len(triangle[i])-1: #最右边
triangle[i][j]=triangle[i-1][j-1]+triangle[i][j]
else: #到达这个位置的路径和=上面两种到达方法更小的那种+这个位置的权值
triangle[i][j]=min(triangle[i-1][j],triangle[i-1][j-1])+triangle[i][j]
return min(triangle[-1])
7.最大正方形 leetcode221
# 在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。
#
# 示例:
#
# 输入:
#
# Testcase Example:
#'[["1","0","1","0","0"],
# ["1","0","1","1","1"],
# ["1","1","1","1","1"],
# ["1","0","0","1","0"]]'
#
# 输出: 4
class Solution(object):
def maximalSquare(self, matrix):
"""
:type matrix: List[List[str]]
:rtype: int
"""
if not matrix:return 0
res=0
row=len(matrix)
col=len(matrix[0])
dp=[[0 for _ in range(col+1)] for _ in range(row+1)]
for i in range(1,row+1):
for j in range(1,col+1):
if matrix[i-1][j-1]=='1': #只有当matrix[i][j]为1,且上面,左边,左上角都不为0的时 #候,才能够组成正方形,而且将dp[i][j]更新为正方形的边长
dp[i][j]=min(int(dp[i-1][j-1]),int(dp[i-1][j]),int(dp[i][j-1]))+1
res=max(res,dp[i][j]) #存一下最大边长
return res*res
8.二位矩阵区域和检索-矩阵不可变 leetcode304
# 给定一个二维矩阵,计算其子矩形范围内元素的总和,该子矩阵的左上角为 (row1, col1) ,右下角为 (row2, col2)。
#
#
# 上图子矩阵左上角 (row1, col1) = (2, 1) ,右下角(row2, col2) = (4, 3),该子矩形内元素的总和为 8。
#
# 示例:
#
# 给定 matrix = [
# [3, 0, 1, 4, 2],
# [5, 6, 3, 2, 1],
# [1, 2, 0, 1, 5],
# [4, 1, 0, 1, 7],
# [1, 0, 3, 0, 5]
# ]
#
# sumRegion(2, 1, 4, 3) -> 8
# sumRegion(1, 1, 2, 2) -> 11
# sumRegion(1, 2, 2, 4) -> 12
#
#
# 说明:
#
#
# 你能够假设矩阵不可变。
# 会屡次调用 sumRegion 方法。
# 你能够假设 row1 ≤ row2 且 col1 ≤ col2。
# 题目说会屡次调用,咱们直接定义一个新矩阵,sums[i][j]表示从matrix[0][0]到此位置围成的正方形的面积,下次计算的时候直接用sums[row2][col2]-sums[row2][col1-1]-sums[row1-1][col2]+sums[row1-1][col1-1]
class NumMatrix(object):
def __init__(self, matrix):
"""
:type matrix: List[List[int]]
"""
if not matrix:
return
m=len(matrix)+1
n=len(matrix[0])+1
self.sums=[[0 for _ in range(n)] for _ in range(m)]
for i in range(1,m):
for j in range(1,n):
self.sums[i][j]=matrix[i-1][j-1]+self.sums[i-1][j]+self.sums[i][j-1]-self.sums[i-1][j-1]
def sumRegion(self, row1, col1, row2, col2):
"""
:type row1: int
:type col1: int
:type row2: int
:type col2: int
:rtype: int
"""
row1, col1, row2, col2=row1+1,col1+1,row2+1,col2+1
return self.sums[row2][col2]-self.sums[row2][col1-1]-self.sums[row1-1][col2]+self.sums[row1-1][col1-1]
# Your NumMatrix object will be instantiated and called as such:
# obj = NumMatrix(matrix)
# param_1 = obj.sumRegion(row1,col1,row2,col2)
9.零钱兑换 leetcode322
# 给定不一样面额的硬币 coins 和一个总金额
# amount。编写一个函数来计算能够凑成总金额所需的最少的硬币个数。若是没有任何一种硬币组合能组成总金额,返回 -1。
#
# 示例 1:
#
# 输入: coins = [1, 2, 5], amount = 11
# 输出: 3
# 解释: 11 = 5 + 5 + 1
#
# 示例 2:
#
# 输入: coins = [2], amount = 3
# 输出: -1
#
# 说明:
# 你能够认为每种硬币的数量是无限的。
#
#
# @lc code=start
class Solution(object):
def coinChange(self, coins, amount):
"""
:type coins: List[int]
:type amount: int
:rtype: int
"""
dp=[float('inf')]*(amount+1)
dp[0]=0
for i in range(1,amount+1):
for coin in coins:
if i>=coin:
dp[i]=min(dp[i],dp[i-coin]+1)
return dp[-1] if dp[-1]!=float('inf') else -1