IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> LeetCode 72. 编辑距离 【c++/java详细题解】 -> 正文阅读

[数据结构与算法]LeetCode 72. 编辑距离 【c++/java详细题解】

1、题目

给你两个单词 word1word2, 请返回将 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
  • word1word2 由小写英文字母组成

2、思路

(动态规划) O ( n ? m ) O(n * m) O(n?m)

给你两个单词 word1word2,我们可以对一个单词进行插入一个字符,删除一个字符,替换一个字符三种操作,请你计算出将 word1 转换成 word2 所使用的最少操作数 。

样例:

如样例所示,word1 = "horse", word2 = "ros",我们将word1转换成word2所使用的最少操作数为3,下面来讲解动态规划的做法。

对于动态规划的题目来说,我们一般要考虑两个问题,分别是状态表示状态计算。状态表示往往和题目的问题相关,因此我们可以定义如下状态表示。

状态表示: f[i][j] 表示将 word1 的前 i 个字符变成 word2 的前 j 个字符所需要进行的最少操作次数。假设word1长度为nword2长度为m,那么f[n][m]就表示将 word1 的前 n 个字符变成 word2 的前 m 个字符所需要进行的最少操作次数,即为答案。

有了状态表示以后,我们去进行状态计算,推导状态计算方程。

状态计算:

如何计算f[i][j]?考虑word1的第i个字符与word2的第j个字符,分为两种情况:

  • 1、word1[i] == word2[j],则f[i][j] == f[i - 1][j - 1];
  • 2、word1[i] != word2[j],我们有三种选择,替换、删除、插入:
    • 替换: 替换word1的第i个字符或者替换word2的第j个字符,则f[i][j] == f[i - 1][j - 1] + 1
    • 删除: 删除word1的第i个字符或者删除word2的第j个字符,则f[i][j] = min(f[i - 1][j], f[i][j - 1]) + 1;
    • 插入:word2[j] 后面添加 word1[i]或者在word1[i]后添加word2[j],则f[i][j] = min(f[i - 1][j], f[i][j - 1]) + 1;

我们去解释一下上述状态计算:

word1[i] == word2[j]时,当前两个字符相同,我们不需要做任何操作,此时f[i][j]就可以从f[i - 1][j - 1]的状态转移过来,换句话说,此时f[i][j]的状态取决于f[i - 1][j - 1]

word1[i] != word2[j]时,此时我们可以进行的操作有三种:

  • 替换: 替换word1的第i个字符或者替换word2的第j个字符,当前位置的字符不匹配,进行替换操作后两者变得相同。

所以f[i][j] == f[i - 1][j - 1] + 1

  • 删除: 删除word1的第i个字符或者删除word2的第j个字符。

    如果当前word1[0 ~ i-1]word2[0 ~ j]匹配,我们删除多余的word1[i],或者word1[0 ~ i]word2[0 ~ j-1]匹配,我们删除多余的word[j]

    两种情况的状态分别为f[i - 1][j]f[i][j - 1],因为题目要求最少操作数,故二者之间我们取一个最小值,所以f[i][j] = min(f[i - 1][j], f[i][j - 1]) + 1

  • 插入:word2[j] 后面添加 word1[i]或者在word1[i]后添加word2[j]

    如果当前word1[0 ~ i-1]word2[0 ~ j]匹配或者word1[0 ~ i]word2[0 ~ j-1]匹配,除了考虑删除多余的字符操作以外,我们还可以执行添加操作,因此添加和删除的状态计算其实是一样的。

    所以f[i][j] = min(f[i - 1][j], f[i][j - 1]) + 1

考虑完状态计算和状态转移以后,接下来我们去进行状态初始化。

3、初始化

for(int i = 0; i <= n; i++)  f[i][0] = i;  //将长度为i的word1变成长度为0的word2需要进行最少i次删除操作
for(int i = 0; i <= m; i++)  f[0][i] = i;  //将长度为i的word2变成长度为0的word1需要进行最少i次删除操作

实现细节:

其实我们可以注意到,word[]数组下标如果从1开始的话,第i个字符就是word[i],而不是下标从0开始的word[i - 1],这样的word[]数组与我们的状态表示会更加相对应。因此,为了代码的可读性更高,我们给word1[]数组和word2[]数组的开头都去添加一个空格,然后在状态计算时,下标从1开始。

时间复杂度分析: 状态数为 O ( n ? m ) O(n * m) O(n?m),状态计算为 O ( 1 ) O(1) O(1),因此总的时间复杂度为 O ( n ? m ) O(n * m) O(n?m)

4、c++代码

class Solution {
public:
    int minDistance(string word1, string word2) {
        int n = word1.size(), m = word2.size();
        word1 = ' ' + word1;  //添加空格
        word2 = ' ' + word2;
        vector<vector<int>>f(n + 1, vector<int>(m + 1));
        for(int i = 0; i <= n; i++)  f[i][0] = i;  //i次删除
        for(int i = 0; i <= m; i++)  f[0][i] = i;  //i次删除 word1 -> word2
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++){
                f[i][j] = min(f[i - 1][j], f[i][j - 1]) + 1;      //添加或者删除
                if(word1[i] == word2[j]) f[i][j] = min(f[i][j], f[i - 1][j - 1]);
                else f[i][j] = min(f[i][j], f[i - 1][j - 1] + 1); //替换
            }
        return f[n][m];    
    }
};

5、Java代码

class Solution {
    public int minDistance(String word1, String word2) {
        int n = word1.length(), m = word2.length();
        word1 = ' ' + word1; //添加空格
        word2 = ' ' + word2;
        int[][] f = new int[n + 10][m + 10];
        for(int i = 0;i <= n;i++) f[i][0] = i; //i次删除
        for(int i = 0;i <= m;i++) f[0][i] = i; //i次删除 word1 -> word2
        for(int i = 1;i <= n;i++)
            for(int j = 1;j <= m;j++)
            {  
                f[i][j] = Math.min(f[i - 1][j] + 1, f[i][j - 1] + 1); //添加或者删除
                if(word1.charAt(i) == word2.charAt(j)) f[i][j] = Math.min(f[i][j], f[i - 1][j - 1]);
                else f[i][j] = Math.min(f[i][j], f[i - 1][j - 1] + 1);//替换
            }
        return f[n][m];
    }
}

原题链接: 72. 编辑距离

在这里插入图片描述

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-01-29 23:20:21  更:2022-01-29 23:20:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 19:18:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码