DP三部曲 | 解释 |
---|---|
设计状态 | f ( i ) f(i) f(i) 表示从 0 0 0 到 i i i 的方案数 |
递推公式 | f ( i ) = f ( i − 1 ) + f ( i − 2 ) f(i) = f(i -1) + f(i -2) f(i)=f(i−1)+f(i−2) |
边界条件 | 当 i = 0 i = 0 i=0 或 i = 1 i = 1 i=1时 f ( i ) f(i) f(i) = 1 |
最终答案 | f ( n ) f(n) f(n) |
方法1:DFS
时间复杂度:
O
(
2
n
)
O(2^n)
O(2n)
空间复杂度:
O
(
n
)
O(n)
O(n)
class Solution:
def climbStairs(self, n: int) -> int:
def dfs(i):
if i == 0 or i == 1:
return 1
return dfs(i - 1) + dfs( i - 2)
return dfs(n)
方法2:记忆化DFS
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
n
)
O(n)
O(n)
class Solution:
def climbStairs(self, n: int) -> int:
@cache
def dfs(i):
if i == 0 or i == 1:
return 1
return dfs(i - 1) + dfs( i - 2)
return dfs(n)
方法3:递推
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
n
)
O(n)
O(n)
class Solution:
def climbStairs(self, n: int) -> int:
dp = [0] * (n + 1)
dp[0] = dp[1] = 1
for i in range(2, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]
方法4:空间优化递推
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
1
)
O(1)
O(1)
class Solution:
def climbStairs(self, n: int) -> int:
f0, f1 = 1, 1
for i in range(2, n + 1):
f0, f1 = f1, f0 + f1
return f1
DP三部曲 | 解释 |
---|---|
设计状态 | f ( i ) f(i) f(i) 表示从 0 0 0 到 i i i 的最小花费 |
递推公式 | f ( i ) = m i n ( f ( i − 1 ) , f ( i − 2 ) ) + c o s t [ i ] f(i) = min(f(i -1), f(i -2)) + cost[i] f(i)=min(f(i−1),f(i−2))+cost[i] |
边界条件 | f ( 0 ) = c o s t [ 0 ] f(0) = cost[0] f(0)=cost[0] 和 f ( 1 ) = c o s t [ 1 ] f(1) = cost[1] f(1)=cost[1] |
最终答案 | m i n ( f ( n − 2 ) , f ( n − 1 ) ) min(f(n-2), f(n-1)) min(f(n−2),f(n−1)) 其中 n n n 表示数组长度 |
方法1:记忆化DFS(递推公式与设计不一样)
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
n
)
O(n)
O(n)
class Solution:
def minCostClimbingStairs(self, cost: List[int]) -> int:
@cache
def dfs(i):
if i == 0 or i == 1:
return 0
return min(dfs(i - 1) + cost[i - 1], dfs(i - 2) + cost[i - 2])
return dfs(len(cost))
方法2:空间优化递推
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
1
)
O(1)
O(1)
class Solution:
def minCostClimbingStairs(self, cost: List[int]) -> int:
n = len(cost); f0, f1 = cost[0], cost[1]
for i in range(2, n):
f0, f1 = f1, min(f0, f1) + cost[i]
return min(f0, f1)
DP三部曲 | 解释 |
---|---|
设计状态 | f ( i ) f(i) f(i) 表示和为 i i i 的方案数 |
递推公式 | f ( i ) = ∑ j = 0 n − 1 f ( i − n u m s [ j ] ) f(i) =\sum_{j=0}^{n-1} f(i - nums[j]) f(i)=∑j=0n−1f(i−nums[j]) 其中 n n n 表示数组长度 |
边界条件 | f ( 0 ) = 1 f(0) = 1 f(0)=1 和 f ( 负数 ) = 0 f(负数) = 0 f(负数)=0 |
最终答案 | f ( t a r g e t ) f(target) f(target) |
方法1:自顶向下记忆化DFS
时间复杂度:
O
(
n
×
t
a
r
g
e
t
)
O(n \times target)
O(n×target)
空间复杂度:
O
(
t
a
r
g
e
t
)
O(target)
O(target)
class Solution:
def combinationSum4(self, nums: List[int], target: int) -> int:
@cache
def dfs(i):
if i == 0:
return 1
if i < 0:
return 0
ans = 0
for x in nums:
ans += dfs(i - x)
return ans
return dfs(target)
方法2:自底向上记忆化DFS
时间复杂度:
O
(
n
×
t
a
r
g
e
t
)
O(n \times target)
O(n×target)
空间复杂度:
O
(
t
a
r
g
e
t
)
O(target)
O(target)
class Solution:
def combinationSum4(self, nums: List[int], target: int) -> int:
@cache
def dfs(val):
if val > target:
return 0
if val == target:
return 1
ans = 0
for x in nums:
ans += dfs(val + x)
return ans
return dfs(0)
方法3:递推
时间复杂度:
O
(
n
×
t
a
r
g
e
t
)
O(n \times target)
O(n×target)
空间复杂度:
O
(
t
a
r
g
e
t
)
O(target)
O(target)
class Solution:
def combinationSum4(self, nums: List[int], target: int) -> int:
dp = [1] + [0] * target
for i in range(1, target + 1):
for x in nums:
if i >= x:
dp[i] += dp[i - x]
return dp[target]
DP三部曲 | 解释 |
---|---|
设计状态 | f ( i ) f(i) f(i) 表示长度为 i i i 的方案数 |
递推公式 | f ( i ) = f ( i − z e r o ) + f ( i − o n e ) f(i) = f(i - zero) + f(i - one) f(i)=f(i−zero)+f(i−one) |
边界条件 | f ( 0 ) = 1 f(0) = 1 f(0)=1 和 f ( 负数 ) = 0 f(负数) = 0 f(负数)=0 |
最终答案 | ∑ i = l o w h i g h f ( i ) \sum_{i=low}^{high} f(i) ∑i=lowhighf(i) |
方法1:递推
时间复杂度:
O
(
h
i
g
h
)
O(high)
O(high)
空间复杂度:
O
(
h
i
g
h
)
O(high)
O(high)
class Solution:
def countGoodStrings(self, low: int, high: int, zero: int, one: int) -> int:
MOD = 10 ** 9 + 7; dp = [1] + [0] * high
for i in range(1, high + 1):
dp[i] = dp[i - zero] if i >= zero else 0
dp[i] += dp[i - one] if i >= one else 0
dp[i] %= MOD
return sum(dp[low:]) % MOD
- LeetCode 2266.统计打字方案数
方法1:分组 + 递推(和LeetCode 377.组合总和 Ⅳ类似) + 乘法原理
时间复杂度: O ( N ) O(N) O(N)
空间复杂度: O ( N ) O(N) O(N)
N = 10 ** 5; MOD = 10 ** 9 + 7
f = [1, 1, 2] + [0] * N
for i in range(3, N + 1):
f[i] = (f[i - 1] + f[i - 2] + f[i - 3]) % MOD
g = [1, 1, 2, 4] + [0] * N
for i in range(4, N + 1):
g[i] = (g[i - 1] + g[i - 2] + g[i - 3] + g[i - 4]) % MOD
class Solution:
def countTexts(self, pressedKeys: str) -> int:
pressedKeys += '#'
ch = pressedKeys[0]; cnt = 0; ans = 1
for s in pressedKeys:
if s == ch:
cnt += 1
else:
if ch == '7' or ch == '9':
ans *= g[cnt]
else:
ans *= f[cnt]
ans %= MOD
ch = s; cnt = 1
return ans % MOD
方法2:分组(使用groupby函数) + 递推(和LeetCode 377.组合总和 Ⅳ类似) + 乘法原理
时间复杂度:
O
(
N
)
O(N)
O(N)
空间复杂度:
O
(
N
)
O(N)
O(N)
N = 10 ** 5; MOD = 10 ** 9 + 7
f = [1, 1, 2] + [0] * N
for i in range(3, N + 1):
f[i] = (f[i - 1] + f[i - 2] + f[i - 3]) % MOD
g = [1, 1, 2, 4] + [0] * N
for i in range(4, N + 1):
g[i] = (g[i - 1] + g[i - 2] + g[i - 3] + g[i - 4]) % MOD
class Solution:
def countTexts(self, pressedKeys: str) -> int:
from itertools import groupby
ans = 1
for ch, idxs in groupby(pressedKeys):
L = len(list(idxs))
if ch == '7' or ch == '9':
ans *= g[L]
else:
ans *= f[L]
ans %= MOD
return ans