Skip to content

Commit

Permalink
更新「线性 DP 知识(一)」相关内容
Browse files Browse the repository at this point in the history
  • Loading branch information
itcharge committed Mar 14, 2023
1 parent 9ee22a2 commit 7ff6d3b
Showing 1 changed file with 98 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@
> **单串线性 DP** 问题:问题的输入为单个数组或单个字符串的线性 DP 问题。状态一般可定义为 $dp[i]$,表示为:
>
> 1. 「以数组中第 $i$ 个位置元素 $nums[i]$ 为结尾的子数组($nums[0]...nums[i]$)」的相关解。
> 2. 「以数组中前 $i$ 个元素为子数组($nums[0]...nums[i - 1]$)」的相关解。
> 2. 「以数组中第 $i - 1$ 个位置元素 $nums[i - 1]$ 为结尾的子数组($nums[0]...nums[i - 1]$)」的相关解。
> 3. 「以数组中前 $i$ 个元素为子数组($nums[0]...nums[i - 1]$)」的相关解。
这两种状态的定义区别在于相差一个元素 $nums[i]$。前者子数组的长度为 $i + 1$,子数组长度不可为空;后者子数组的长度为 $i$,子数组长度可为空。后者在 $i = 0$ 时,方便用于表示空数组(以数组中前 $0$ 个元素为子数组)。
这 $3$ 种状态的定义区别在于相差一个元素 $nums[i]$。

1. 第 $1$ 种状态:子数组的长度为 $i + 1$,子数组长度不可为空;
2. 第 $2$ 种状态、第 $3$ 种状态:这两种状态描述是相同的。子数组的长度为 $i$,子数组长度可为空。在 $i = 0$ 时,方便用于表示空数组(以数组中前 $0$ 个元素为子数组)。

### 2.1 最长递增子序列

Expand Down Expand Up @@ -77,7 +81,7 @@

对于满足 $0 \le j < i$ 的数组元素 $nums[j]$ 和 $nums[i]$ 来说:

- 如果 $nums[j] < nums[i]$,则 $nums[i]$ 可以接在 $nums[j]$ 后面,此时以 $nums[i]$ 结尾的最长递增子序列长度会在「以 $nums[j]$ 结尾的最长递增子序列长度」的基础上加 $1$,即 $dp[i] = dp[j] + 1$。
- 如果 $nums[j] < nums[i]$,则 $nums[i]$ 可以接在 $nums[j]$ 后面,此时以 $nums[i]$ 结尾的最长递增子序列长度会在「以 $nums[j]$ 结尾的最长递增子序列长度」的基础上加 $1$,即$dp[i] = dp[j] + 1$。

- 如果 $nums[j] \le nums[i]$,则 $nums[i]$ 不可以接在 $nums[j]$ 后面,可以直接跳过。

Expand Down Expand Up @@ -440,9 +444,13 @@ class Solution:
> **双串线性 DP 问题**:问题的输入为两个数组或两个字符串的线性 DP 问题。状态一般可定义为 $dp[i][j]$,表示为:
>
> 1. 「以第一个数组中第 $i$ 个位置元素 $nums1[i]$ 为结尾的子数组($nums1[0]...nums1[i]$)」与「以第二个数组中第 $j$ 个位置元素 $nums2[j]$ 为结尾的子数组($nums2[0]...nums2[j]$)」的相关解。
> 2. 「以第一个数组中前 $i$ 个元素为子数组($nums1[0]...nums1[i - 1]$)」与「以第二个数组中前 $j$ 个元素为子数组($nums2[0]...nums2[j - 1]$)」的相关解。
> 2. 「以第一个数组中第 $i - 1$ 个位置元素 $nums1[i - 1]$ 为结尾的子数组($nums1[0]...nums1[i - 1]$)」与「以第二个数组中第 $j - 1$ 个位置元素 $nums2[j - 1]$ 为结尾的子数组($nums2[0]...nums2[j - 1]$)」的相关解。
> 3. 「以第一个数组中前 $i$ 个元素为子数组($nums1[0]...nums1[i - 1]$)」与「以第二个数组中前 $j$ 个元素为子数组($nums2[0]...nums2[j - 1]$)」的相关解。
这 $3$ 种状态的定义区别在于相差一个元素 $nums1[i]$ 或 $nums2[j]$。

这两种状态的定义区别在于相差一个元素 $nums1[i]$ 或 $nums2[j]$。前者子数组的长度为 $i + 1$ 或 $j + 1$,子数组长度不可为空;后者子数组的长度为 $i$ 或 $j$,子数组长度可为空。后者在 $i = 0$ 或 $j = 0$ 时,方便用于表示空数组(以数组中前 $0$ 个元素为子数组)。
1. 第 $1$ 种状态:子数组的长度为 $i + 1$ 或 $j + 1$,子数组长度不可为空
2. 第 $2$ 种状态、第 $3$ 种状态:子数组的长度为 $i$ 或 $j$,子数组长度可为空。$i = 0$ 或 $j = 0$ 时,方便用于表示空数组(以数组中前 $0$ 个元素为子数组)。

### 3.1 最长公共子序列

Expand Down Expand Up @@ -622,54 +630,116 @@ class Solution:

### 3.3 编辑距离

双串线性 DP 问题中除了经典的最长公共子序列问题之外,还包括字符串的模糊匹配问题。

#### 3.3.1 题目链接

- [72. 编辑距离 - 力扣](https://leetcode.cn/problems/edit-distance/)

#### 3.3.2 题目大意

#### 3.3.3 解题思路
**描述**:给定两个单词 $word1$、$word2$。

对一个单词可以进行以下三种操作:

- 插入一个字符
- 删除一个字符
- 替换一个字符

**要求**:计算出将 $word1$ 转换为 $word2$ 所使用的最少操作数。

**说明**

- $0 \le word1.length, word2.length \le 500$。
- $word1$ 和 $word2$ 由小写英文字母组成。

## 4. 矩阵线性 DP问题
**示例**

- 示例 1:

```Python
输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')
```

- 示例 2:

```Python
输入:word1 = "intention", word2 = "execution"
输出:5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')
```

> **矩阵线性 DP 问题**:问题的输入为二维矩阵的线性 DP 问题。状态一般可定义为 $dp[i][j]$,表示为:从「位置 $(0, 0)$」到达「位置 $(i, j)$」的相关解。
#### 3.3.3 解题思路

### 4.1 最小路径和
##### 思路 1:动态规划

#### 4.1.1 题目链接
###### 1. 划分阶段

#### 4.1.2 题目大意
按照两个字符串的结尾位置进行阶段划分。

#### 4.1.3 解题思路
###### 2. 定义状态

### 4.2 最大正方形
定义状态 $dp[i][j]$ 表示为:「以 $word1$ 中前 $i$ 个字符组成的子字符串 $str1$」变为「以 $word2$ 中前 $j$ 个字符组成的子字符串 $str2$」,所需要的最少操作次数。

#### 4.2.1 题目链接
###### 3. 状态转移方程

#### 4.2.2 题目大意
1. 如果当前字符相同($word1[i - 1] = word2[j - 1]$),无需插入、删除、替换。$dp[i][j] = dp[i - 1][j - 1]$。
2. 如果当前字符不同($word1[i - 1] \ne word2[j - 1]$),$dp[i][j]$ 取源于以下三种情况中的最小情况:
1. 替换($word1[i - 1]$ 替换为 $word2[j - 1]$):最少操作次数依赖于「以 $word1$ 中前 $i - 1$ 个字符组成的子字符串 $str1$」变为「以 $word2$ 中前 $j - 1$ 个字符组成的子字符串 $str2$」,再加上替换的操作数 $1$,即:$dp[i][j] = dp[i - 1][j - 1] + 1$。
2. 插入($word1$ 在第 $i - 1$ 位置上插入元素):最少操作次数依赖于「以 $word1$ 中前 $i - 1$ 个字符组成的子字符串 $str1$」 变为「以 $word2$ 中前 $j$ 个字符组成的子字符串 $str2$」,再加上插入需要的操作数 $1$,即:$dp[i][j] = dp[i - 1][j] + 1$。
3. 删除($word1$ 删除第 $i - 1$ 位置元素):最少操作次数依赖于「以 $word1$ 中前 $i$ 个字符组成的子字符串 $str1$」变为「以 $word2$ 中前 $j - 1$ 个字符组成的子字符串 $str2$」,再加上删除需要的操作数 $1$,即:$dp[i][j] = dp[i][j - 1] + 1$。

#### 4.2.3 解题思路
综合上述情况,状态转移方程为:

## 5. 无串线性 DP 问题
$dp[i][j] = \begin{cases} dp[i - 1][j - 1] & word1[i - 1] = word2[j - 1] \cr min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]) + 1 & word1[i - 1] \ne word2[j - 1] \end{cases}$

> **无串线性 DP 问题**:问题的输入不是显式的数组或字符串,但依然可分解为若干子问题的线性 DP 问题。
###### 4. 初始条件

### 5.1 整数拆分
- 当 $i = 0$,「以 $word1$ 中前 $0$ 个字符组成的子字符串 $str1$」为空字符串,「$str1$」变为「以 $word2$ 中前 $j$ 个字符组成的子字符串 $str2$」时,至少需要插入 $j$ 次,即:$dp[0][j] = j$。
- 当 $j = 0$,「以 $word2$ 中前 $0$ 个字符组成的子字符串 $str2$」为空字符串,「以 $word1$ 中前 $i$ 个字符组成的子字符串 $str1$」变为「$str2$」时,至少需要删除 $i$ 次,即:$dp[i][0] = i$。

#### 5.1.1 题目链接
###### 5. 最终结果

#### 5.1.2 题目大意
根据状态定义,最后输出 $dp[sise1][size2]$(即 $word1$ 变为 $word2$ 所使用的最少操作数)即可。其中 $size1$、$size2$ 分别为 $word1$、$word2$ 的字符串长度。

#### 5.1.3 解题思路
##### 思路 1:代码

### 5.2 只有两个键的键盘
```Python
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
size1 = len(word1)
size2 = len(word2)
dp = [[0 for _ in range(size2 + 1)] for _ in range(size1 + 1)]

#### 5.2.1 题目链接
for i in range(size1 + 1):
dp[i][0] = i
for j in range(size2 + 1):
dp[0][j] = j
for i in range(1, size1 + 1):
for j in range(1, size2 + 1):
if word1[i - 1] == word2[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
else:
dp[i][j] = min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]) + 1
return dp[size1][size2]
```

#### 5.2.2 题目大意
##### 思路 1:复杂度分析

#### 5.2.3 解题思路
- **时间复杂度**:$O(n \times m)$,其中 $n$、$m$ 分别是字符串 $word1$、$word2$ 的长度。两重循环遍历的时间复杂度是 $O(n \times m)$,所以总的时间复杂度为 $O(n \times m)$。
- **空间复杂度**:$O(n \times m)$。用到了二维数组保存状态,所以总体空间复杂度为 $O(n \times m)$。

## 参考资料

- 【书籍】算法竞赛进阶指南
- 【文章】[动态规划概念和基础线性DP | 潮汐朝夕](https://chengzhaoxi.xyz/1a4a2483.html)

- 【文章】[动态规划概念和基础线性DP | 潮汐朝夕](https://chengzhaoxi.xyz/1a4a2483.html)

0 comments on commit 7ff6d3b

Please sign in to comment.