62. 不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 "Start" )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 "Finish" )。
问总共有多少条不同的路径?
- 【思路】
python
# from functools import cache
from functools import lru_cache as cache # 3.9以下的版本
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
@cache
def dfs(i, j): # 表示从(0,0)走到(i,j)的路径数
if i < 0 or j < 0: # 无法从(0,0)走到这些位置
return 0
if i == 0 and j == 0: # dfs(0, 0) 原地不动是一种办法
return 1
return dfs(i-1, j) + dfs(i, j-1)
return dfs(m-1, n-1)
- 时间复杂度 O(mn)
- 空间复杂度 O(mn)
64. 最小路径和
给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
参考自灵茶山艾府
python
# 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 inf
if 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"
中心扩散法
参考自灵茶山艾府
写法一:奇偶分开判断
python
class Solution:
def longestPalindrome(self, s: str) -> str:
n = len(s)
ans_left = ans_right = 0
# 奇回文串
for i in range(n):
l = r = i
while l >= 0 and r < n and s[l] == s[r]:
l -= 1
r += 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 + 1
while l >= 0 and r < n and s[l] == s[r]:
l -= 1
r += 1
if 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 。
参考自灵茶山艾府
python
# 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 0
if s[i] == t[j]:
return dfs(i - 1, j - 1) + 1
return 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')
参考自灵茶山艾府
python
# 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 + 1
if j < 0:
return i + 1
if 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)) + 1
return dfs(n - 1, m - 1)
- 时间复杂度 O(mn),其中 n 为 s 的长度,m 为 t 的长度。
- 空间复杂度 O(mn)