本文主要是介绍代码随想录算法训练营Day55 | 583.两个字符串的删除操作、72.编辑距离,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
583.两个字符串的删除操作
最开始想到的是基于最长公共子序列的写法:删除公共子序列以外的字符,两个字符串就相同了
int minDistance0(string word1, string word2) {int n = word1.size();int m = word2.size();vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));for (int i = 1; i <= n; ++i) {for (int j = 1; j <= m; ++j) {if (word1[i - 1] == word2[j - 1])dp[i][j] = dp[i - 1][j - 1] + 1;elsedp[i][j] = std::max(dp[i][j - 1], dp[i - 1][j]); }}// 删除最长公共子序列以外的字符return n + m - 2 * dp[n][m];
}
另一种基于题意定义DP数组的写法:
题目求需要进行删除的最小操作数,那么就将DP数组定义为目前的最小删除次数
1、DP数组定义: dp[i][j] 表示以word2[j - 1] 为结尾的子串和 word1[i - 1] 为结尾的子串达到相同需要的最小删除操作次数
2、DP数组初始化:dp[0][0]初始化为0,其余首列与首行元素初始化为i / j(有 i / j 个字符的字符串与一个空字符串达到相同需要进行 i / j 次删除操作)
3、递推公式:
· 当word1[i - 1] == word2[j - 1]时,不需要进行删除操作:
dp[i][j] = dp[i - 1][j - 1]
· 当word1[i - 1] != word2[j - 1]时,dp[i][j]可以由三个方向取最小转移得到:
方向1——dp[i][j - 1],在此基础上删除 word1[i - 1]
方向2——dp[i - 1][j],在此基础上删除 word2[j - 1]
方向3——dp[i - 1][j - 1],在此基础上删除 word1[i - 1] 和 word2[j - 1]
最后的递推公式:dp[i][j] = min(dp[i - 1][j - 1] + 2, min(dp[i][j - 1] + 1, dp[i - 1][j] + 1))
4、遍历顺序:i 依赖 i - 1,j 依赖 j - 1,所以从左向右从上向下遍历
int minDistance(string word1, string word2) {// dp[i][j]表示达到相同需要的最小删除操作次数vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1, 0));// 除dp[0][0]外,dp[i][0]和dp[0][j]初始化为i/jfor (int i = 1; i <= word1.size(); ++i)dp[i][0] = i;for (int j = 1; j <= word2.size(); ++j)dp[0][j] = j;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];// 三个方向取最小值elsedp[i][j] = std::min(dp[i - 1][j - 1] + 2, std::min(dp[i][j - 1] + 1, dp[i - 1][j] + 1));}}return dp[word1.size()][word2.size()];
}
72.编辑距离
这题仍然是根据题意定义DP数组,重点是理清楚删除、替换、插入三种操作的状态转移
1、DP数组定义: dp[i][j] 表示以 word1[i - 1] 为结尾的子串想要达到与 word2[j - 1] 为结尾的子串相同,需要的最小编辑次数
2、DP数组初始化:dp[0][0]初始化为0,其余首列与首行元素初始化为i / j(有 i / j 个字符的字符串与一个空字符串达到相同需要进行 i / j 次删除操作)
3、递推公式:
· 当word1[i - 1] == word2[j - 1]时,不需要进行编辑操作:
dp[i][j] = dp[i - 1][j - 1]
· 当word1[i - 1] != word2[j - 1]时,dp[i][j]可以由三种操作取最小转移得到:
删除 —— 将word[i - 1]删除,在 dp[i - 1][j] 的基础上+1,
替换 —— 将 word1[i - 1] 替换为 word2[j - 1],在 dp[i - 1][i - 1] 的基础上+1
插入 —— 将一个等于 word2[j - 1] 的值插在原先word1[i - 1]的位置上,在 dp[i][j - 1] 的基础上+1
最后的递推公式:dp[i][j] = min(dp[i - 1][j] + 1, min(dp[i - 1][j - 1] + 1, dp[i][j - 1] + 1))
4、遍历顺序:i 依赖 i - 1,j 依赖 j - 1,所以从左向右从上向下遍历
int minDistance(string word1, string word2) {// dp[i][j]表示达到相同需要的最小编辑操作次数vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1, 0));for (int i = 1; i <= word1.size(); ++i)dp[i][0] = i;for (int j = 1; j <= word2.size(); ++j)dp[0][j] = j;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 - 1][j] + 1 (在dp[i - 1][j] + 1的基础上删除word[i - 1])// 替换:dp[i - 1][j - 1] + 1 (在dp[i - 1][i - 1]的基础上将word1[i - 1]替换为word2[j - 1])// 插入:dp[i][j - 1] + 1 (在dp[i][j - 1]的基础上插入一个等于word2[j - 1]的值)dp[i][j] = std::min(dp[i - 1][j] + 1, std::min(dp[i - 1][j - 1] + 1, dp[i][j - 1] + 1));}}}return dp[word1.size()][word2.size()];
}
编辑距离总结
这类题目做多了还是能找到些套路的:
1、DP数组定义:
· DP数组的定义一般是题目要求什么就定义成什么,
· dp[i][j] 一般表示的是以 word1[i - 1] 为结尾的子串和 word2[j - 1] 为结尾的子串
2、DP数组初始化:结合题意,一般首行和首列的初始化最为重要
3、递推公式:
分析状态转移可以分为“基础”和“新增”两部分:
· 基础:继承之前的状态,如果当前值匹配一般只要进行这步操作
· 新增:在之前状态的基础上增加操作时新增的值,如果当前值不匹配一般需要额外进行这步操作
4、遍历顺序:结合题意,一般是 i 依赖 i - 1,j 依赖 j - 1,所以大部分情况是从左向右从上向下遍历
这篇关于代码随想录算法训练营Day55 | 583.两个字符串的删除操作、72.编辑距离的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!