Skip to content
2 changes: 1 addition & 1 deletion Solutions/0045. 跳跃游戏 II.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@

初始状态下,跳到下标 `0` 需要的最小跳跃次数为 `0`,即 `dp[0] = 0`。

##### 5. 最终结果
###### 5. 最终结果

根据我们之前定义的状态,`dp[i]` 表示为:跳到下标 `i` 所需要的最小跳跃次数。则最终结果为 `dp[size - 1]`。

Expand Down
68 changes: 51 additions & 17 deletions Solutions/0091. 解码方法.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,76 @@

## 题目大意

给定一个数字字符串 s,按照要求映射为 26 个字母。`1` 映射为 `A`,`2` 映射为 `B`,...,`26` 映射为 `Z`。
**描述**:给定一个数字字符串 `s`,按照要求映射为 `26` 个字母。`1` 映射为 `A`,`2` 映射为 `B`,...,`26` 映射为 `Z`。

要求:计算出共有多少种可能的翻译方案。
**要求**:计算出共有多少种可能的翻译方案。

**说明**:

- $1 \le s.length \le 100$。
- `s` 只包含数字,并且可能包含前导零。
- 题目数据保证答案肯定是一个 `32` 位的整数。

**示例**:

```Python
输入 s = "226"
输出 3
解释 它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。
```

## 解题思路

可用动态规划来做。设 dp[i] 表示字符串 s 前 i 个字符 s[0: i] 的翻译方案数。dp[i] 的来源有两种情况:
### 思路 1:动态规划

###### 1. 划分阶段

按照字符串的结尾位置进行阶段划分。

1. 对 s[i] 进行翻译。那么只要 s[i] != 0,就可以被翻译为 A~I 的某个字母,此时方案数为 dp[i] = dp[i-1]
2. 对 s[i-1] 和 s[i] 进行翻译,s[i-1] != 0,且 s[i-1] 和 s[i] 组成的整数必须小于等于 26 才能翻译,这样才能翻译为 J~Z 中的某字母,此时方案数为 dp[i] = dp[i-2]。
###### 2. 定义状态

定义状态 `dp[i]` 表示为:字符串 `s` 前 `i` 个字符构成的字符串可能构成的翻译方案数。

###### 3. 状态转移方程

`dp[i]` 的来源有两种情况:

1. 使用了一个字符,对 `s[i]` 进行翻译。只要 `s[i] != 0`,就可以被翻译为 `A` ~ `I` 的某个字母,此时方案数为 `dp[i] = dp[i - 1]`。
2. 使用了两个字符,对 `s[i - 1]` 和 `s[i]` 进行翻译,只有 `s[i - 1] != 0`,且 `s[i - 1]` 和 `s[i]` 组成的整数必须小于等于 `26` 才能翻译,可以翻译为 `J` ~ `Z` 中的某字母,此时方案数为 `dp[i] = dp[i - 2]`。

这两种情况有可能是同时存在的,也有可能都不存在。在进行转移的时候,将符合要求的方案数累加起来即可。

状态转移方程可以写为:

$dp[i] += \begin{cases} \begin{array} \ dp[i-1] & s[i] \ne 0 \cr dp[i-2] & s[i-1] \ne 0,s[i-1:i] \le 26 \end{array} \end{cases}$

现在考虑初始边界条件。字符串为空时,只有一个翻译方案,翻译为空字符串,即 dp[0] = 1。
###### 4. 初始条件

字符串只有一个字符时,需要考虑该字符是否为 0,不为 0 的话,dp[1] = 1,为 0 的话,dp[0] = 0。
- 字符串为空时,只有一个翻译方案,翻译为空字符串,即 `dp[0] = 1`。
- 字符串只有一个字符时,需要考虑该字符是否为 `0`,不为 `0` 的话,`dp[1] = 1`,为 `0` 的话,`dp[0] = 0`。

字符串长度大于等于 2 时,就需要根据上边的状态转移方程来获取了,最终答案为 dp[n]。
###### 5. 最终结果

## 代码
根据我们之前定义的状态,`dp[i]` 表示为:字符串 `s` 前 `i` 个字符构成的字符串可能构成的翻译方案数。则最终结果为 `dp[size]`,`size` 为字符串长度。


### 思路 1:动态规划代码

```Python
class Solution:
def numDecodings(self, s: str) -> int:
n = len(s)
dp = [0 for _ in range(n+1)]
size = len(s)
dp = [0 for _ in range(size + 1)]
dp[0] = 1
for i in range(1, n+1):
if s[i-1] != '0':
dp[i] += dp[i-1]
if i > 1 and s[i-2] != '0' and int(s[i-2:i]) <= 26:
dp[i] += dp[i-2]
return dp[n]
for i in range(1, size + 1):
if s[i - 1] != '0':
dp[i] += dp[i - 1]
if i > 1 and s[i - 2] != '0' and int(s[i - 2: i]) <= 26:
dp[i] += dp[i - 2]
return dp[size]
```

### 思路 1:复杂度分析

- **时间复杂度**:$O(n)$。一重循环遍历的时间复杂度是 $O(n)$。
- **空间复杂度**:$O(n)$。用到了一维数组保存状态,所以总体空间复杂度为 $O(n)$。
2 changes: 1 addition & 1 deletion Solutions/0198. 打家劫舍.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
- 如果只有一间房,则直接偷这间屋子就能偷到最高金额,即 `dp[0] = nums[i]`。
- 如果只有两间房,那么就选择金额最大的那间屋进行偷窃,就可以偷到最高金额,即 `dp[1] = max(nums[0], nums[1])`。

##### 5. 最终结果
###### 5. 最终结果

根据我们之前定义的状态,`dp[i]` 表示为:前 `i` 间房屋所能偷窃到的最高金额。则最终结果为 `dp[size - 1]`,`size` 为总的房屋数。

Expand Down
2 changes: 1 addition & 1 deletion Solutions/0213. 打家劫舍 II.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
- 如果只有一间房,则直接偷这间屋子就能偷到最高金额,即 `dp[0] = nums[i]`。
- 如果只有两间房,那么就选择金额最大的那间屋进行偷窃,就可以偷到最高金额,即 `dp[1] = max(nums[0], nums[1])`。

##### 5. 最终结果
###### 5. 最终结果

根据我们之前定义的状态,`dp[i]` 表示为:前 `i` 间房屋所能偷窃到的最高金额。假设求解 `[0, size - 2]` 和 `[1, size - 1]` 范围下( `size` 为总的房屋数)首尾不相连的房屋所能偷窃的最高金额问题分别为 `ans1`、`ans2`,则最终结果为 `max(ans1, ans2)`。

Expand Down
2 changes: 1 addition & 1 deletion Solutions/0300. 最长递增子序列.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@

默认状态下,把数组中的每个元素都作为长度为 `1` 的递增子序列。即 `dp[i] = 1`。

##### 5. 最终结果
###### 5. 最终结果

根据我们之前定义的状态,`dp[i]` 表示为:以 `nums[i]` 结尾的最长递增子序列长度。那为了计算出最大的最长递增子序列长度,则需要再遍历一遍 `dp` 数组,求出最大值即为最终结果。

Expand Down
2 changes: 1 addition & 1 deletion Solutions/0338. 比特位计数.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@

整数 `0` 对应二进制表示中 `1` 的个数为 `0`。

##### 5. 最终结果
###### 5. 最终结果

整个 `dp` 数组即为最终结果,将其返回即可。

Expand Down
2 changes: 1 addition & 1 deletion Solutions/0403. 青蛙过河.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@

刚开始青蛙站在序号为 `0` 石子上(即 `stones[0]`),肯定能以长度为 `0` 的距离,到达第 `0` 块石子,即 `dp[0][0] = True`。

##### 5. 最终结果
###### 5. 最终结果

根据我们之前定义的状态,`dp[i][k]` 表示为:青蛙能否以长度为 `k` 的距离,到达第 `i` 块石子。则如果 `dp[size - 1][k]` 为真,则说明青蛙能成功过河(即能在最后一步跳到最后一块石子上);否则则说明青蛙不能成功过河。

Expand Down
85 changes: 85 additions & 0 deletions Solutions/0576. 出界的路径数.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# [0576. 出界的路径数](https://leetcode.cn/problems/out-of-boundary-paths/)

- 标签:动态规划
- 难度:中等

## 题目大意

**描述**:有一个大小为 `m * n` 的网络和一个球。球的起始位置为 `[startRow, startColumn]`。你可以将球移到在四个方向上相邻的单元格内(可以穿过网格边界到达网格之外)。最多可以移动 `maxMove` 次球。

现在给定五个整数 `m`、`n`、`maxMove`、`startRow` 以及 `startColumn`。

**要求**:找出并返回可以将球移出边界的路径数量。因为答案可能非常大,返回对 $10^9 + 7$ 取余后的结果。

**说明**:

- $1 \le m, n \le 50$。
- $0 \le maxMove \le 50$。
- $0 \le startRow < m$。
- $0 \le startColumn < n$。

**示例**:

```Python
输入 m = 2, n = 2, maxMove = 2, startRow = 0, startColumn = 0
输出 6
```

![](https://assets.leetcode.com/uploads/2021/04/28/out_of_boundary_paths_1.png)

## 解题思路

### 思路 1:动态规划

我们需要统计从 `[startRow, startColumn]` 位置出发,最多移动 `maxMove` 次能够穿过边界的所有路径数量。则我们可以根据位置和移动步数来划分阶段和定义状态。

###### 1. 划分阶段

按照位置进行阶段划分。

###### 2. 定义状态

定义状态 `dp[i][j][k]` 表示为:从位置 `[i, j]` 最多移动 `k` 次最终穿过边界的所有路径数量。

###### 3. 状态转移方程

因为球可以在上下左右四个方向上进行移动,所以对于位置 `[i, j]`,最多移动 `k` 次最终穿过边界的所有路径数量取决于周围四个方向上最多经过 `k - 1` 次穿过对应位置上的所有路径数量和。

即 `dp[i][j][k] = dp[i - 1][j][k - 1] + dp[i + 1][j][k - 1] + dp[i][j - 1][k - 1] + dp[i][j + 1][k - 1]`。

###### 4. 初始条件

如果位置 `[i, j]` 已经处于边缘,只差一步就穿过边界。则此时位置 `[i, j]` 最多移动 `k` 次最终穿过边界的所有路径数量取决于有相邻多少个方向是边界。也可以通过对上面 `[i - 1, j]`、`[i + 1][j]`、`[i][j - 1]`、`[i, j + 1]` 是否已经穿过边界进行判断(每一个方向穿过一次,就累积一次),来计算路径数目。然后将其作为初始条件。

###### 5. 最终结果

根据我们之前定义的状态,`dp[i][j][k]` 表示为:从位置 `[i, j]` 最多移动 `k` 次最终穿过边界的所有路径数量。则最终答案为 `dp[startRow][startColumn][maxMove]`。

### 思路 1:动态规划代码

```Python
class Solution:
def findPaths(self, m: int, n: int, maxMove: int, startRow: int, startColumn: int) -> int:
directions = {(1, 0), (-1, 0), (0, 1), (0, -1)}
mod = 10 ** 9 + 7

dp = [[[0 for _ in range(maxMove + 1)] for _ in range(n)] for _ in range(m)]
for i in r
for k in range(1, maxMove + 1):
for i in range(m):
for j in range(n):
for direction in directions:
new_i = i + direction[0]
new_j = j + direction[1]
if 0 <= new_i < m and 0 <= new_j < n:
dp[i][j][k] = (dp[i][j][k] + dp[new_i][new_j][k - 1]) % mod
else:
dp[i][j][k] = (dp[i][j][k] + 1) % mod

return dp[startRow][startColumn][maxMove]
```

### 思路 1:复杂度分析

- **时间复杂度**:$O(m * n * maxMove)$。三重循环遍历的时间复杂度为 $O(m * n * maxMove)$。
- **空间复杂度**:$O(m * n * maxMove)$。使用了三维数组保存状态,所以总体空间复杂度为 $O(m * n * maxMove)$。
8 changes: 4 additions & 4 deletions Solutions/0674. 最长连续递增序列.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@

### 思路 1:动态规划

##### 1. 定义状态
###### 1. 定义状态

定义状态 `dp[i]` 表示为:以 `nums[i]` 结尾的最长且连续递增的子序列长度。

##### 2. 状态转移方程
###### 2. 状态转移方程

因为求解的是连续子序列,所以只需要考察相邻元素的状态转移方程。

Expand All @@ -45,11 +45,11 @@

综上,我们的状态转移方程为:`dp[i] = dp[i - 1] + 1`,`nums[i - 1] < nums[i]`。

##### 3. 初始化
###### 3. 初始条件

默认状态下,把数组中的每个元素都作为长度为 `1` 的最长且连续递增的子序列长度。即 `dp[i] = 1`。

##### 4. 最终结果
###### 4. 最终结果

根据我们之前定义的状态,`dp[i]` 表示为:以 `nums[i]` 结尾的最长且连续递增的子序列长度。则为了计算出最大值,则需要再遍历一遍 `dp` 数组,求出最大值即为最终结果。

Expand Down
8 changes: 4 additions & 4 deletions Solutions/1143. 最长公共子序列.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@

### 思路 1:动态规划

##### 1. 定义状态
###### 1. 定义状态

定义状态 `dp[i][j]` 表示为:前 `i` 个字符组成的字符串 `str1` 与前 `j` 个字符组成的字符串 `str2` 的最长公共子序列长度为 `dp[i][j]`。

##### 2. 状态转移方程
###### 2. 状态转移方程

双重循环遍历字符串 `text1` 和 `text2`,则状态转移方程为:

Expand All @@ -41,11 +41,11 @@
- `text1` 前 `i - 1` 个字符组成的字符串 `str1` 与 `text2` 前 `j` 个字符组成的 `str2` 的最长公共子序列长度,即 `dp[i - 1][j]`。
- `text1` 前 `i` 个字符组成的字符串 `str1` 与 `text2` 前 `j - 1` 个字符组成的 str2 的最长公共子序列长度,即 `dp[i][j - 1]`。

##### 3. 初始化
###### 3. 初始化

初始状态下,默认前 `i` 个字符组成的字符串 `str1` 与前 `j` 个字符组成的字符串 `str2` 的最长公共子序列长度为 `0`,即 `dp[i][j] = 0`。

##### 4. 最终结果
###### 4. 最终结果

根据状态定义,最后输出 `dp[sise1][size2]`(即 `text1` 与 `text2` 的最长公共子序列长度)即可,其中 `size1`、`size2` 分别为 `text1`、`text2` 的字符串长度。

Expand Down