leetcode 72. 编辑距离
编辑距离题解集合
动态规划
解题思路:
- 定义数组元素含义
dp[i][j] 代表 word1 到 i 位置转换成 word2 到 j 位置需要最少步数
- 找出关系数组元素间的关系式
因为我们的目标是:从规模小的,通过一些操作,推导出规模大的。对于这道题,我们可以对word1进行三种操作: 插入一个字符 删除一个字符 替换一个字符 由于我们是要让操作的次数最小,所以我们要寻找最佳操作。那么有如下关系式: (一)、当word1[i]==word2[j]时,由于遍历到了i和j,说明word1的0~ i-1和word2的0~ j-1的匹配结果已经生成, 由于当前两个字符相同,因此无需做任何操作,dp[i][j]=dp[i-1][j-1] (二)、当word1[i]!=word2[j]时,可以进行的操作有3个: ① 替换操作:可能word1的0~ i-1位置与word2的0~j-1位置的字符都相同, 只是当前位置的字符不匹配,进行替换操作后两者变得相同, 所以此时dp[i][j]=dp[i-1][j-1]+1(这个加1代表执行替换操作) ②删除操作:若此时word1的0~ i-1位置与word2的0~j位置已经匹配了, 此时多出了word1的i位置字符,应把它删除掉,才能使此时word1的0~i(这个i是执行了删除操作后新的i) 和word2的0~j位置匹配,因此此时dp[i][j]=dp[i-1][j]+1(这个加1代表执行删除操作) ③插入操作:若此时word1的0~ i位置只是和word2的0~j-1位置匹配, 此时只需要在原来的i位置后面插入一个和word2的j位置相同的字符使得 此时的word1的0~ i(这个i是执行了插入操作后新的i)和word2的0~j匹配得上, 所以此时dp[i][j]=dp[i][j-1]+1(这个加1代表执行插入操作) ④由于题目所要求的是要最少的操作数:所以当word1[i] != word2[j] 时, 需要在这三个操作中选取一个最小的值赋格当前的dp[i][j] (三)总结:状态方程为:
if(word1[i] == word2[j]):
dp[i][j] = dp[i-1][j-1]
else:
min(dp[i-1][j-1],dp[i-1][j],dp[i][j-1])+1
- 找出初始值
显然,当dp[i][j]中,如果i或者j有一个为0,那么还能使用关系式吗? 显然不能,因为i-1或者j-1为负数,所有我们的初始值计算出所有的dp[0][0…n]和dp[0…n][0]。 这个还是非常容易计算的,因为当有一个字符串的长度为0时,转化为另外一个字符串,那么就只需要一直进行插入或者删除操作即可。
class Solution {
public:
int minDistance(string word1, string word2)
{
vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1));
for (int i = 0; i < dp.size(); i++)
dp[i][0] = i;
for (int j = 0; j < dp[0].size(); j++)
dp[0][j] = j;
for (int i = 1; i < dp.size(); ++i)
{
for (int j = 1; j < dp[0].size(); ++j)
{
//这里i-1和j-1表示从第一个字符开始进行比较,因为字符串里面的第一个字符下标为0
if (word1[i - 1] == word2[j - 1])
//这里dp里面的i和j表示从word1开始的第i个字符,和从word2开始的第j个字符
//如果比较的是第一个字符,那么dp里面的i和j都为1,
dp[i][j] = dp[i - 1][j - 1];
else
dp[i][j] = min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1])) + 1;
}
}
return dp.back().back();
}
};
动态规划的优化—优化为一维数组
计算第i行的值,我们只需要用到第i-1行的值,那么就可以采用一维dp来处理。
由上图可以看出,计算dp[i][j]需要用到三个地方的值,分别为(i-1,j),(i,j-1)和(i-1,j-1),如果把当前二维数组简化为一维的化,我们需要利用到dp[i-1]未更新前的值,dp[i-1]更新后的值和dp[i]未更新前的值。 其中dp[i-1]未更新前的值会被dp[i-1]更新后的值覆盖掉,因此我们需要用一个pre变量保存dp[i-1]未更新前的值 那么这里dp[i-1]未更新前的值对应(i-1,j-1) dp[i-1]更新后的值对应(i-1,j) dp[i]未更新前的值对应(i,j-1)
dp[i]=min(dp[i-1],pre,dp[i])+1;
下面看代码:
class Solution {
public:
int minDistance(string word1, string word2)
{
vector<int> dp(word2.size()+1);
//word2长度作为行的长度
//word1长度作为列的长度
//计算第一行的长度,我们这里是一行行求出来的,第二行依赖第一行求出
for (int i = 0; i <= word2.size(); ++i)
dp[i]=i;
//外层循环每执行一次,就求解新的一行,因为第一行已经求解出来了,所以从第二行开始求解
for (int i = 1; i <= word1.size(); ++i)
{
//内存循环从当前行第二个元素开始求解
//注意我们保存第一行的第一个元素,相当于保存(i-1,j-1)
int temp = dp[0];
//更新dp[0]为当前行第一个元素的值
dp[0] = i;
for (int j = 1; j <= word2.size(); ++j)
{
int pre = temp;//保存dp(i-1,j-1)
temp = dp[j];
//字符串字符下标从0开始
if (word1[i - 1] == word2[j - 1])
dp[j] = pre;
else//dp[j-1]对应dp(i-1,j) pre对应dp(i-1,j-1) dp[j]对应dp(i,j-1)
dp[j] = min(dp[j - 1], min(pre, dp[j])) + 1;
}
}
return dp.back();
}
};
相关文章
- 在 Go 里用 CGO?这 7 个问题你要关注!
- 9款优秀的去中心化通讯软件 Matrix 的客户端
- 求职数据分析,项目经验该怎么写
- 在OKR中,我看到了数据驱动业务的未来
- 火山引擎云原生大数据在金融行业的实践
- OpenHarmony富设备移植指南(二)—从postmarketOS获取移植资源
- 《数据成熟度指数》报告:64%的企业领袖认为大多数员工“不懂数据”
- OpenHarmony 小型系统兼容性测试指南
- 肯睿中国(Cloudera):2023年企业数字战略三大趋势预测
- 适用于 Linux 的十大命令行游戏
- GNOME 截图工具的新旧截图方式
- System76 即将推出的 COSMIC 桌面正在酝酿大变化
- 2GB 内存 8GB 存储即可流畅运行,Windows 11 极致精简版系统 Tiny11 发布
- 迎接 ecode:一个即将推出的具有全新图形用户界面框架的现代、轻量级代码编辑器
- loongarch架构介绍(三)—地址翻译
- Go 语言怎么解决编译器错误“err is shadowed during return”?
- 敏捷:可能被开发人员遗忘的部分
- Denodo预测2023年数据管理和分析的未来
- 利用数据推动可持续发展
- 在 Vue3 中实现 React 原生 Hooks(useState、useEffect),深入理解 React Hooks 的