经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
编辑距离算法
来源:cnblogs  作者:Kellen_Gram  时间:2024/3/26 8:57:45  对本文有异议

1.题目

给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数  

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

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

示例:

  1. 输入:word1 = "horse", word2 = "ros"
  2. 输出:3
  3. 解释:
  4. horse -> rorse (将 'h' 替换为 'r')
  5. rorse -> rose (删除 'r')
  6. rose -> ros (删除 'e')

链接:https://leetcode.cn/problems/edit-distance/description/

2.分析

??可以转化为一道多维的动态规划问题,在两个字符串的删除操作的基础上添加了删除和替换操作。我们可以在二维的基础上额外添加一个变量来表示操作类型

??k = 0,删除操作,k = 1替换操作,k = 2插入操作

1.确定dp数组

??dp[i][j][k] 表示在word1 [0..i] ,word2 [0..j] 的子串执行k操作后满足两个字符串相等的最小操作数,k = 0,1,2

??例如:

? word1 = "h" , word2 = "r",dp[1][1][0] 表示执行删除操作后,word1 和 word2 相等的最小操作数,显然 dp[1][1][0] = 2

? 要记住 k 操作对应的是最后一个操作

??在使用动态规划的时候,在清晰 dp[i][j]考虑的是 i,j是末尾的状态的值,不要去考虑对别的值的影响,例如  dp[k][p] (k ≥ i 和  p≥ j的情况)

2.确定转换公式

转换可以分为两个情况, word1[i] == word2[j] 和  word1[i] ≠ word2[j]

1.word1[i] == word2[j]

如果 word1[i] == word2[j]了,那么我们其实不需要进行任何操作,此时取前一个状态的最小值就好

  1. int tmp = min(min(dp[i - 1][j - 1][0], dp[i - 1][j - 1][1]), dp[i - 1][j - 1][2]);
  2. dp[i][j][0] = tmp;
  3. dp[i][j][1] = tmp;
  4. dp[i][j][2] = tmp;

2.word1[i] ≠ word2[j]

word1[i] ≠ word2[j]时,可以拆分为插入,删除和替换三种情况

2.1 删除

??删除操作对应dp[i][j]时删除 i 或者 删除 j,那么只要考虑 dp[i-1][j],dp[i][j-1]的情况即可(注意这里 dp[i-1][j],dp[i][j-1] 都可能进行多种操作)

  1. int tmp2 = min(min(dp[i - 1][j][1], dp[i - 1][j][2]), dp[i - 1][j][0]);
  2. int tmp3 = min(min(dp[i][j-1][1], dp[i][j-1][2]), dp[i][j-1][0]);
  3. dp[i][j][0] = min(tmp2, tmp3) + 1;

2.2 替换

对于[i][j]进行替换,那么我们只需要替换 i 或者 替换 j 就可以了,替换就是在 [0..i-1] [0..j-1]的基础上,加上一个操作使得 i == j

  1. int tmp1 = min(min(dp[i - 1][j - 1][0], dp[i - 1][j - 1][1]), dp[i - 1][j - 1][2]);
  2. dp[i][j][1] = tmp1 + 1;

2.3 插入

对于插入操作,本质和删除一致的,为什么这么说呢?

??word1[i] ≠ word2[j],我们只能在 i 或者 j 的尾部进行插入,即 i-1 插入字符 char 使得 char  == word2[j];或者 j-1 位置插入字符  char 使得 char  == word1[i]

??如果在 i 或者 j后面插入,我们还需要额外进行一次删除操作,因此插入操作代码和删除一致,这里可以进行优化

  1. int tmp2 = min(min(dp[i - 1][j][1], dp[i - 1][j][2]), dp[i - 1][j][0]);
  2. int tmp3 = min(min(dp[i][j-1][1], dp[i][j-1][2]), dp[i][j-1][0]);
  3. dp[i][j][2] = min(tmp2, tmp3) + 1;

 3. 初始化

考虑到dp会用到前面的数据,便于递推额外添加一个大小,因此 dp初始化为  vector<vector<vector<int>>> dp(word1.size() + 1, vector<vector<int>>(word2.size()+ 1, vector<int>(3)));

??word1取[0..0]的时候,word1为空字符串“”;word2只能删除全部字符,或者word2替换全部字符为 " " 空字符串,或者word1插入和word2一样的字符

??同理word2取[0..0]的时候也是

  1. vector<vector<vector<int>>> dp(M + 1, vector<vector<int>>(N + 1, vector<int>(3)));
  2. //每一步都可能执行不同的操作 这时候替换表示地替换成 "" 空字符串,插入表示对另一边进行插入
  3. for (int i = 0; i <= M; ++i)
  4. {
  5. dp[i][0][0] = i;
  6. dp[i][0][1] = i;
  7. dp[i][0][2] = i;
  8. }
  9. for (int j = 0; j <= N; ++j)
  10. {
  11. dp[0][j][0] = j;
  12. dp[0][j][1] = j;
  13. dp[0][j][2] = j;
  14. }

3. 代码实现

  1. class Solution {
  2. public:
  3. int minDistance(string word1, string word2) {
  4. //dp[i][j][k] 表示 [0..i] [0..j]相同所需的最少操作符 k表示执行这个操作时最小值
  5. //最后只需要对三个数 求最小值
  6. const int M = word1.size();
  7. const int N = word2.size();
  8. vector<vector<vector<int>>> dp(M + 1, vector<vector<int>>(N + 1, vector<int>(3)));
  9. //每一步都可能执行不同的操作 这时候替换表示地替换成 "" 空字符串,插入表示对另一边进行插入
  10. // 0-插入 1 -删除 2-替换
  11. for (int i = 0; i <= M; ++i)
  12. {
  13. dp[i][0][0] = i;
  14. dp[i][0][1] = i;
  15. dp[i][0][2] = i;
  16. }
  17. for (int j = 0; j <= N; ++j)
  18. {
  19. dp[0][j][0] = j;
  20. dp[0][j][1] = j;
  21. dp[0][j][2] = j;
  22. }
  23. for (int i = 1; i <= M; ++i)
  24. {
  25. for (int j = 1; j <= N; ++j)
  26. {
  27. if (word1[i-1] == word2[j-1])
  28. {
  29. int tmp = min(min(dp[i - 1][j - 1][0], dp[i - 1][j - 1][1]), dp[i - 1][j - 1][2]);
  30. dp[i][j][0] = tmp;
  31. dp[i][j][1] = tmp;
  32. dp[i][j][2] = tmp;
  33. }
  34. else
  35. {
  36. int tmp2 = min(min(dp[i - 1][j][1], dp[i - 1][j][2]), dp[i - 1][j][0]);
  37. int tmp3 = min(min(dp[i][j-1][1], dp[i][j-1][2]), dp[i][j-1][0]);
  38. dp[i][j][0] = min(tmp2, tmp3) + 1;
  39. //替换 在i-1,j-1的基础上,替换值
  40. int tmp1 = min(min(dp[i - 1][j - 1][0], dp[i - 1][j - 1][1]), dp[i - 1][j - 1][2]);
  41. dp[i][j][1] = tmp1 + 1;
  42. //插入 - 最优情况只能在少的一边插入,否则会增加一个删除操作
  43. //删除和插入本质应该一致,因为都应该在尾部插,否则增加额外一个删除操作
  44. dp[i][j][2] = dp[i][j][0];
  45. }
  46. }
  47. }
  48. return min(min(dp[M][N][0], dp[M][N][1]), dp[M][N][2]);
  49. }
  50. };

 

原文链接:https://www.cnblogs.com/Kellen-Gram/p/18095787

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号