LeetCode Hot 100 | 动态规划(下)(C++ 题解)
目录
前言
动态规划下篇涵盖最长有效括号、编辑距离、不同路径、最小路径和、最长回文子串和最长公共子序列六道经典题目,重点掌握二维 DP 的状态转移设计。
1. 32. 最长有效括号 🔴
题目描述
给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
示例 1:
输入:s = "(()"
输出:2
示例 2:
输入:s = ")()())"
输出:4
约束: 0 <= s.length <= 3 * 10^4
解题思路
len[i] 表示以 s[i] 结尾的最长有效括号长度(仅 s[i]==')' 时非零):
- 若
s[i-1]=='(':len[i] = len[i-2] + 2 - 若
s[i-1]==')'且m = i - len[i-1] - 1处为(:len[i] = len[i-1] + 2 + len[m-1]
以 s = "(()())" 为例:
| i | s[i] | s[i-1] | m | len[i] |
|---|---|---|---|---|
| 1 | ‘)’ | ‘(’ | - | 0+2=2 |
| 2 | ‘(’ | ‘)’ | - | 0 |
| 3 | ‘)’ | ‘(’ | - | len[1]+2=4 |
| 4 | ‘(’ | ‘)’ | - | 0 |
| 5 | ‘)’ | ‘(’ | - | len[3]+2=6 |
ans=6 ✓
复杂度分析
- 时间: O(n)
- 空间: O(n)
C++ 代码
class Solution {
public:
int longestValidParentheses(string s) {
int n = s.size();
vector<int> len(n, 0);
int ans = 0;
for (int i = 1; i < n; i++) {
if (s[i] == ')') {
if (s[i - 1] == '(') {
len[i] = i >= 2 ? len[i - 2] + 2 : 2;
} else {
int m = i - len[i - 1] - 1;
if (m >= 0 && s[m] == '(')
len[i] = m - 1 >= 0 ? len[i - 1] + 2 + len[m - 1] : len[i - 1] + 2;
}
}
ans = max(ans, len[i]);
}
return ans;
}
};
2. 72. 编辑距离 🟡
题目描述
给你两个单词 word1 和 word2,请返回将 word1 转换成 word2 所使用的最少操作数。你可以对一个单词进行如下三种操作:插入一个字符、删除一个字符、替换一个字符。
示例 1:
输入:word1 = "horse", word2 = "ros"
输出:3(horse→rorse→rose→ros)
示例 2:
输入:word1 = "intention", word2 = "execution"
输出:5
约束: 0 <= word1.length, word2.length <= 500

解题思路
f[i][j] 表示 word1[0..i-1] 转换为 word2[0..j-1] 的最少操作数。
- 初始化:
f[0][j]=j(全插入),f[i][0]=i(全删除) - 若
word1[i-1]==word2[j-1]:f[i][j] = f[i-1][j-1] - 否则:
f[i][j] = min(f[i-1][j], f[i][j-1], f[i-1][j-1]) + 1
以 word1="ab", word2="bc" 为例:
| “” | b | c | |
|---|---|---|---|
| “” | 0 | 1 | 2 |
| a | 1 | 1 | 2 |
| b | 2 | 1 | 2 |
返回 f[2][2]=2 ✓
复杂度分析
- 时间: O(m × n)
- 空间: O(m × n)
C++ 代码
class Solution {
public:
int minDistance(string word1, string word2) {
int m = word1.size();
int n = word2.size();
vector<vector<int>> f(m + 1, vector<int>(n + 1, 0));
for(int i = 1; i <= n; i++) {
f[0][i] = f[0][i - 1] + 1;
}
for (int i = 1; i <=m; i++) {
f[i][0] = f[i - 1][0] + 1;
for (int j = 1; j <= n; j++) {
if (word1[i - 1] == word2[j - 1]) {
f[i][j] = f[i - 1][j - 1];
} else {
f[i][j] = min(min(f[i - 1][j], f[i][j - 1]), f[i - 1][j - 1]) + 1;
}
}
}
return f[m][n];
}
};
3. 62. 不同路径 🟡
题目描述
一个机器人位于一个 m x n 网格的左上角。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角。问总共有多少条不同的路径?
示例 1:
输入:m = 3, n = 7
输出:28
示例 2:
输入:m = 3, n = 2
输出:3
约束: 1 <= m, n <= 100

解题思路
dp[i][j] 表示到达 (i,j) 的路径数,等于 dp[i-1][j] + dp[i][j-1]。边界全为 1。
以 m=3, n=3 为例(实际为 (m+1)×(n+1) 的辅助数组,起点偏移):
| 1 | 2 | 3 | |
|---|---|---|---|
| 1 | 1 | 1 | 1 |
| 2 | 1 | 2 | 3 |
| 3 | 1 | 3 | 6 |
返回 dp[3][3]=6 ✓
复杂度分析
- 时间: O(m × n)
- 空间: O(m × n)
C++ 代码
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 1));
for (int i = 2; i <= m; i++) {
for (int j = 2; j <= n; j++) {
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m][n];
}
};
4. 64. 最小路径和 🟡
题目描述
给定一个包含非负整数的 m x n 网格 grid,请找出一条从左上角到右下角的路径,使得路径上的数字总和最小。每次只能向下或向右移动一步。
示例 1:
输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7(1→3→1→1→1)
示例 2:
输入:grid = [[1,2,3],[4,5,6]]
输出:12
约束: m == grid.length, n == grid[i].length, 1 <= m, n <= 200

解题思路
原地 DP:先处理第一行(累加),再处理第一列(累加),然后对其余位置取 min(上, 左) + 当前值。
以 grid = [[1,3,1],[1,5,1],[4,2,1]] 为例:
| 0 | 1 | 2 | |
|---|---|---|---|
| 0 | 1 | 4 | 5 |
| 1 | 2 | 7 | 6 |
| 2 | 6 | 8 | 7 |
返回 grid[2][2]=7 ✓
复杂度分析
- 时间: O(m × n)
- 空间: O(1)(原地)
C++ 代码
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
int m = grid.size();
int n = grid[0].size();
for (int i = 1; i < n; i++) {
grid[0][i] += grid[0][i - 1];
}
for (int i = 1; i < m; i++) {
grid[i][0] += grid[i - 1][0];
for (int j = 1; j < n; j++) {
grid[i][j] = min(grid[i - 1][j], grid[i][j - 1]) + grid[i][j];
}
}
return grid[m - 1][n - 1];
}
};
5. 5. 最长回文子串 🟡
题目描述
给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = "babad"
输出:"bab"(或"aba")
示例 2:
输入:s = "cbbd"
输出:"bb"
约束: 1 <= s.length <= 1000, s 仅由数字和英文字母组成

解题思路
中心扩展法:对每个位置,分别以奇数长度(单字符中心)和偶数长度(双字符中心)向外扩展,记录最长回文的起始位置和长度。
以 s = "babad" 从 i=1 (a) 奇数扩展为例:
| left | right | s[left] | s[right] | 操作 |
|---|---|---|---|---|
| 1 | 1 | ‘a’ | ‘a’ | 相等,len=1, start=1 |
| 0 | 2 | ‘b’ | ‘b’ | 相等,len=3, start=0 |
| -1 | 3 | 越界 | - | 停止 |
找到 “bab”,len=3 ✓
复杂度分析
- 时间: O(n²)
- 空间: O(1)
C++ 代码
class Solution {
public:
string longestPalindrome(string s) {
int left = 0;
int right = 0;
int start = 0;
int len = 0;
for (int i = 0; i < s.size(); i++) {
left = i;
right = i;
while(left >=0 && right < s.size() && s[left] == s[right]) {
if (right - left + 1 > len) {
len = right - left + 1;
start = left;
}
left--;
right++;
}
left = i;
right = i + 1;
while(left >= 0 && right < s.size() && s[left] == s[right]) {
if (right - left + 1 > len){
len = right - left + 1;
start = left;
}
left--;
right++;
}
}
return s.substr(start, len);
}
};
6. 1143. 最长公共子序列 🟡
题目描述
给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列,返回 0。
示例 1:
输入:text1 = "abcde", text2 = "ace"
输出:3("ace")
示例 2:
输入:text1 = "abc", text2 = "abc"
输出:3
约束: 1 <= text1.length, text2.length <= 1000

解题思路
f[i+1][j+1] 表示 text1[0..i] 与 text2[0..j] 的LCS长度:
- 若
text1[i]==text2[j]:f[i+1][j+1] = f[i][j] + 1 - 否则:
f[i+1][j+1] = max(f[i][j+1], f[i+1][j])
以 text1="ace", text2="ace" 为例:
| “” | a | c | e | |
|---|---|---|---|---|
| “” | 0 | 0 | 0 | 0 |
| a | 0 | 1 | 1 | 1 |
| c | 0 | 1 | 2 | 2 |
| e | 0 | 1 | 2 | 3 |
返回 f[3][3]=3 ✓
复杂度分析
- 时间: O(m × n)
- 空间: O(m × n)
C++ 代码
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int m = text1.size();
int n = text2.size();
vector<vector<int>> f(text1.size() + 1, vector<int>(text2.size() + 1, 0));
for (int i = 0; i < text1.size(); i++) {
for (int j = 0; j < text2.size(); j++) {
if (text1[i] == text2[j])
f[i + 1][j + 1] = f[i][j] + 1;
else
f[i + 1][j + 1] = max(f[i][j + 1], f[i + 1][j]);
}
}
return f[m][n];
}
};
总结
| 题号 | 题目 | 难度 | 核心技巧 | 时间复杂度 |
|---|---|---|---|---|
| 32 | 最长有效括号 | 🔴困难 | DP,区分前一字符是否为’(’ | O(n) |
| 72 | 编辑距离 | 🟡中等 | 二维DP,三种操作对应三个方向 | O(m×n) |
| 62 | 不同路径 | 🟡中等 | 二维DP,路径数=上+左 | O(m×n) |
| 64 | 最小路径和 | 🟡中等 | 原地DP,取min(上,左)+当前 | O(m×n) |
| 5 | 最长回文子串 | 🟡中等 | 中心扩展,奇偶两种情况 | O(n²) |
| 1143 | 最长公共子序列 | 🟡中等 | 二维DP,字符相等则+1否则取max | O(m×n) |
(C++ 题解)&spm=1001.2101.3001.5002&articleId=159908006&d=1&t=3&u=a80706d0f34c44df9f01824823ddc246)
387

被折叠的 条评论
为什么被折叠?



