動態規劃整理
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