力扣hot100 | 多维动态规划 | 62. 不同路径、64. 最小路径和、5. 最长回文子串、1143. 最长公共子序列、72. 编辑距离
62. 不同路径
力扣题目链接
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
- 【思路】
# from functools import cache
from functools import lru_cache as cache # 3.9以下的版本class Solution:def uniquePaths(self, m: int, n: int) -> int:@cachedef dfs(i, j): # 表示从(0,0)走到(i,j)的路径数if i < 0 or j < 0: # 无法从(0,0)走到这些位置return 0if i == 0 and j == 0: # dfs(0, 0) 原地不动是一种办法return 1return dfs(i-1, j) + dfs(i, j-1)return dfs(m-1, n-1)
- 时间复杂度 O(mn)
- 空间复杂度 O(mn)
64. 最小路径和
力扣题目链接
给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
参考自灵茶山艾府
# from functools import cache
from functools import lru_cache as cache # 3.9以下的版本class Solution:def minPathSum(self, grid: List[List[int]]) -> int:@cache def dfs(i: int, j: int) -> int:if i < 0 or j < 0:return infif i == 0 and j == 0:return grid[i][j]return min(dfs(i, j - 1), dfs(i - 1, j)) + grid[i][j]return dfs(len(grid) - 1, len(grid[0]) - 1)
- 时间复杂度 O(mn),其中 m 和 n 分别为 grid 的行数和列数。由于每个状态只会计算一次,动态规划的
时间复杂度 = 状态个数 × 单个状态的计算时间
。本题状态个数等于 O(mn),单个状态的计算时间为 O(1),所以总的时间复杂度为 O(mn)。 - 空间复杂度 O(mn),保存多少状态,就需要多少空间。
5. 最长回文子串
力扣题目链接
给你一个字符串 s
,找到 s
中最长的 回文 子串。
示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
示例 2:
输入:s = “cbbd”
输出:“bb”
中心扩散法
参考自灵茶山艾府
写法一:奇偶分开判断
class Solution:def longestPalindrome(self, s: str) -> str:n = len(s)ans_left = ans_right = 0# 奇回文串for i in range(n):l = r = iwhile l >= 0 and r < n and s[l] == s[r]:l -= 1r += 1# 循环结束后,s[l+1] 到 s[r-1] 是回文串if r - l - 1 > ans_right - ans_left:ans_left, ans_right = l + 1, r # 左闭右开区间# 偶回文串for i in range(n - 1):l, r = i, i + 1while l >= 0 and r < n and s[l] == s[r]:l -= 1r += 1if r - l - 1 > ans_right - ans_left:ans_left, ans_right = l + 1, r # 左闭右开区间return s[ans_left: ans_right]
- 时间复杂度 O(n^2)
- 空间复杂度 O(1)
1143. 最长公共子序列
力扣题目链接
给定两个字符串 text1
和 text2
,返回这两个字符串的最长公共子序列的长度。如果不存在公共子序列 ,返回0
。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
示例 1:
输入:text1 = “abcde”, text2 = “ace”
输出:3
解释:最长公共子序列是 “ace” ,它的长度为 3 。
示例 2:
输入:text1 = “abc”, text2 = “abc”
输出:3
解释:最长公共子序列是 “abc” ,它的长度为 3 。
示例 3:
输入:text1 = “abc”, text2 = “def”
输出:0
解释:两个字符串没有公共子序列,返回 0 。
参考自灵茶山艾府
# from functools import cache
from functools import lru_cache as cache # 3.9以下的版本class Solution:def longestCommonSubsequence(self, s: str, t: str) -> int:n, m = len(s), len(t)@cache def dfs(i: int, j: int) -> int: # s[:i+1]与t[:j+1]的最长公共子序列长度if i < 0 or j < 0:return 0if s[i] == t[j]:return dfs(i - 1, j - 1) + 1return max(dfs(i - 1, j), dfs(i, j - 1))return dfs(n - 1, m - 1)
- 时间复杂度 O(nm)
- 空间复杂度 O(nm)
72. 编辑距离
力扣题目链接
给你两个单词 word1
和 word2
, 请返回将 word1
转换成 word2
所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
- 插入一个字符
- 删除一个字符
- 替换一个字符
示例 1:
输入:word1 = “horse”, word2 = “ros”
输出:3
解释:
horse -> rorse (将 ‘h’ 替换为 ‘r’)
rorse -> rose (删除 ‘r’)
rose -> ros (删除 ‘e’)
示例 2:
输入:word1 = “intention”, word2 = “execution”
输出:5
解释:
intention -> inention (删除 ‘t’)
inention -> enention (将 ‘i’ 替换为 ‘e’)
enention -> exention (将 ‘n’ 替换为 ‘x’)
exention -> exection (将 ‘n’ 替换为 ‘c’)
exection -> execution (插入 ‘u’)
参考自灵茶山艾府
# from functools import cache
from functools import lru_cache as cache # 3.9以下的版本class Solution:def minDistance(self, s: str, t: str) -> int:n, m = len(s), len(t)@cache def dfs(i: int, j: int) -> int: # 将word1[:i+1]转成word2[:j+1]的最少操作数if i < 0:return j + 1if j < 0:return i + 1if s[i] == t[j]:return dfs(i - 1, j - 1)return min(dfs(i - 1, j), dfs(i, j - 1), dfs(i - 1, j - 1)) + 1return dfs(n - 1, m - 1)
- 时间复杂度 O(mn),其中 n 为 s 的长度,m 为 t 的长度。
- 空间复杂度 O(mn)