20天 hot 100 速通计划-day19
多维动态规划
62. 不同路径
一个机器人位于一个 m x n
网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
示例 1:
输入:m = 3, n = 7
输出:28
示例 2:
输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下
示例 3:
输入:m = 7, n = 3
输出:28
示例 4:
输入:m = 3, n = 3
输出:6
提示:
1 <= m, n <= 100
- 题目数据保证答案小于等于
2 * 109
强用动规结构
class Solution {
public:
int uniquePaths(int m, int n) {
// 定义:dp数组表示到 (0,0) 到 (i,j) 的不同路径数
vector<vector<int>> dp(m, vector<int>(n, 0));
// 定初值:将第一行和第一列的路径数初始化为1,因为只有一条路径可以到达
for(int i = 0; i < m; i++) {
dp[i][0] = 1;
}
for(int j = 0; j < n; j++) {
dp[0][j] = 1;
}
// 定序:从左上角开始逐步计算每个位置的路径数
for(int i = 1; i < m; i++){
for(int j = 1; j < n; j++){
// 定式:到达当前位置的路径数等于上方位置的路径数加上左方位置的路径数
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
return dp[m-1][n-1];
}
};
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
0 <= grid[i][j] <= 200
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
// 定义:dp[i][j]表示到达格子(i, j)的最小路径和
// 定式:dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]
// 定初值:dp[0][0] = grid[0][0]
// 定序:从左到右,从上到下遍历
int m = grid.size();
int n = grid[0].size();
// 初始化dp数组
vector<vector<int>> dp(m, vector<int>(n, 0));
dp[0][0] = grid[0][0];
// 遍历计算每个格子的最小路径和
for (int i = 1; i < m; i++) {
dp[i][0] = dp[i-1][0] + grid[i][0];
}
for (int j = 1; j < n; j++) {
dp[0][j] = dp[0][j-1] + grid[0][j];
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j];
}
}
return dp[m-1][n-1];
}
};
5. 最长回文子串
给你一个字符串 s
,找到 s
中最长的回文子串。
如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。
示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd"
输出:"bb"
提示:
1 <= s.length <= 1000
s
仅由数字和英文字母组成
class Solution {
public:
string longestPalindrome(string s) {
int n = s.length();
// 定义:dp[i][j]表示从字符串s的第i个字符到第j个字符是否为回文串
vector<vector<bool>> dp(n, vector<bool>(n, false));
// 定义:start记录最长回文子串的起始位置,maxLen记录最长回文子串的长度
int start = 0, maxLen = 1;
// 定初值:单个字符肯定是回文串,所以将dp[i][i]设置为true
for (int i = 0; i < n; i++) {
dp[i][i] = true;
}
// 定义:对dp状态数组进行遍历,外层循环从后往前,内层循环从前往后
for (int i = n - 2; i >= 0; i--) {
for (int j = i + 1; j < n; j++) {
// 定式:如果s[i]等于s[j]且dp[i+1][j-1]为true(即s[i+1]到s[j-1]是回文串),则dp[i][j]为true
if (s[i] == s[j] && (j - i == 1 || dp[i+1][j-1])) {
dp[i][j] = true;
// 如果当前回文串的长度大于maxLen,则更新最长回文子串的起始位置和长度
if (j - i + 1 > maxLen) {
maxLen = j - i + 1;
start = i;
}
}
}
}
// 返回最长回文子串
return s.substr(start, maxLen);
}
}
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 。
提示:
1 <= text1.length, text2.length <= 1000
text1
和text2
仅由小写英文字符组成。
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
// 定义:dp[i][j]表示text1的前i个字符与text2的前j个字符的最长公共子序列的长度
// dp[i][j]: i-1 j-1 为结尾最长公共子序列长度
vector<vector<int>> dp(text1.size() + 1, vector<int>(text2.size() + 1, 0));
// 定初值:当i=0或j=0时,dp[i][j]=0,表示一个字符串为空时,最长公共子序列长度为0
for(int i = 0; i <= text1.size(); i++){
dp[i][0] = 0;
}
for(int j = 0; j <= text2.size(); j++){
dp[0][j] = 0;
}
// 定式:dp[i][j] = dp[i-1][j-1] + 1,如果text1[i-1] == text2[j-1]
// dp[i][j] = max(dp[i-1][j], dp[i][j-1]),如果text1[i-1] != text2[j-1]
for(int i = 1; i <= text1.size(); i++){
for(int j = 1; j <= text2.size(); j++){
if(text1[i - 1]==text2[j - 1]){
dp[i][j] = dp[i-1][j-1] + 1;
}
//不要求连续,需要回头
else{
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
}
return dp[text1.size()][text2.size()];
}
};
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')
提示:
0 <= word1.length, word2.length <= 500
word1
和word2
由小写英文字母组成
class Solution {
public:
//正统编辑距离
//受限操作为两个字符串可以删除、插入或替换
// 定义:dp[i][j] 表示将 word1 的前 i 个字符转换成 word2 的前 j 个字符所需的最少操作数
// 定式:dp[i][j]=min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1])+1,当 word1[i-1] != word2[j-1] 时
// dp[i][j] = dp[i-1][j-1],当 word1[i-1] == word2[j-1] 时
// 定初值:dp[0][j] = j,将空串转换成 word2 的前 j 个字符所需的最少操作数
// dp[i][0] = i,将 word1 的前 i 个字符转换成空串所需的最少操作数
// 定序:从小到大依次遍历 i 和 j,计算 dp[i][j]
int minDistance(string word1, string word2) {
// i-1 j-1 最少操作数
vector<vector<int>> dp(word1.size()+1, vector<int>(word2.size()+1));
//dp[0][j]:删成空串
//dp[i][0]:删成空串
for(int j = 0; j<= word2.size(); j++){
dp[0][j] = j;
}
for(int i = 0; i <= word1.size(); i++){
dp[i][0] = i;
}
for(int i = 1; i <= word1.size(); i++){
for(int j = 1; j<= word2.size(); j++){
// 相等不用操作
if(word1[i-1] == word2[j-1]){
dp[i][j] = dp[i-1][j-1];
}else{
dp[i][j] = min({
dp[i][j-1], // 删 word2 等价于 增 word1
dp[i-1][j], // 删 word1
dp[i-1][j-1], //替换
})+1;
}
}
}
return dp[word1.size()][word2.size()];
}
}