583.两个字符串的删除操作 72.编辑距离

583.两个字符串的删除操作 72.编辑距离

583.两个字符串的删除操作

力扣题目链接(opens new window)

给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。

示例:

  • 输入: “sea”, “eat”
  • 输出: 2
  • 解释: 第一步将"sea"变为"ea",第二步将"eat"变为"ea"

思路

思路:动态规划
动态规划五部曲
1.定义dp数组以及下标含义
定义二维dp数组。dp[i][j]表示以字符word1[i]为结尾字符串和以字符word2 [j]为结尾字符串中,相同所需的最小步数
2。定义递推公式
情况一:word1[i] == word2 [j]
dp[i][j] = dp[i-1][j-1]
情况二:word1[i] != word2 [j]
dp[i][j] = Math.min(dp[i-1][j] + 1,dp[i][j-1] + 1);
3.初始化dp数组
考虑二维数组第一行和第一列如何初始化
第一行
第一个元素
if (word1.charAt(0) != word2.charAt(0)) {
    dp[0][0] = 2;
}
剩余元素
如果word1.charAt(i) == word2.charAt(0),删除字符步数为i
如果word1.charAt(i) != word2.charAt(0),【删除字符步数】跟【dp[0][0]】也就是word1.charAt(i) == word2.charAt(0)是否相等有关
1.相等。那么删除字符步数为dp[i][0] = i。举例如下 word1:abc word2:a.
dp[0][0] = 0.dp[1][0]表示删除ab等于a的步数,很显然为1,也就是i
可以得出dp[i][0] = i
2.不相等。那么删除字符步骤相比dp[i-1][0]应增加1.举例如下 word1:abc word2:t
dp[0][0] = 2.dp[1][0]表示删除ab等于t的步数,很显然为3,也就是dp[0][0] +1
可以得出dp[1][0] = dp[0][0] +1.即 dp[i][0] = dp[i - 1][0] + 1;
for (int i = 1; i < word1.length(); i++) {
    if (word1.charAt(i) == word2.charAt(0)) {
          dp[i][0] = i;
    } else {
         if (dp[0][0] == 0) {
            dp[i][0] = i;
         } else {
             dp[i][0] = dp[i - 1][0] + 1;
          }
    }
   }

第一列 同理
4.遍历顺序
有递推公式可知
从小到大遍历即可
5.举例推导dp数组
时间复杂度O(N^2)
空间复杂度O(N^2)

代码如下

public static void main(String args[]) {
    minDistance("sea", "ate");
}

public static int minDistance(String word1, String word2) {
    if (word1 == null || word2 == null)// 边界条件处理
        return 0;
    if (word1.equals("") && !word2.equals(""))
        return word2.length();
    if ((!word1.equals("") && word2.equals("")))
        return word1.length();

    int[][] dp = new int[word1.length()][word2.length()];// 定义Dp数组以及初始化
    if (word1.charAt(0) != word2.charAt(0)) {
        dp[0][0] = 2;
    }
    for (int i = 1; i < word1.length(); i++) {
        if (word1.charAt(i) == word2.charAt(0)) {
            dp[i][0] = i;
        } else {
            if (dp[0][0] == 0) {
                dp[i][0] = i;
            } else {
                dp[i][0] = dp[i - 1][0] + 1;
            }
        }
    }
    for (int j = 1; j < word2.length(); j++) {
        if (word2.charAt(j) == word1.charAt(0)) {
            dp[0][j] = j;
        } else {
            if (dp[0][0] == 0) {
                dp[0][j] = j;
            } else {
                dp[0][j] = dp[0][j - 1] + 1;
            }
        }
    }

    for (int i = 1; i < word1.length(); i++) {
        for (int j = 1; j < word2.length(); j++) {
            if (word1.charAt(i) == word2.charAt(j)) {
                dp[i][j] = dp[i - 1][j - 1];
            } else {
                dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1);
            }
        }
    }
    return dp[word1.length() - 1][word2.length() - 1];

}

问题

问题1
本题目在推导dp公式时出现了错误
错误推导方式
情况二:word1[i] != word2 [j]
dp[i][j] = Math.min(dp[i-1][j],dp[i][j-1]);
正确方式
忘记给步数加一了
dp[i][j] = Math.min(dp[i-1][j] + 1,dp[i][j-1] + 1);

问题2
初始化错误,初始化有点复杂
错误方式
没考虑到word1.charAt(i) != word2.charAt(0)时
【删除字符步数dp[i][0]】跟【dp[0][0]】有关

优化

本题目属于编辑距离类题目。只有删除操作,对word1和word2都有删除操作
二刷时采用另一种定义dp数组的方式、
dp[i][j]表示以word1[i-1]和word2[i-1]为结尾字符串中,使得 word1 和 word2 相同所需的最小步数
采用这种定义dp数组方式,可简化初始化dp数组流程
从递推公式中,可以看出来,dp[i][0] 和 dp[0][j]是一定要初始化的。
dp[i][0]:word2为空字符串,以i-1为结尾的字符串word1要删除多少个元素,才能和word2相同呢,很明显dp[i][0] = i。
dp[0][j]的话同理

代码如下

public static int minDistance(String word1, String word2) {

    if (word1 == null || word2 == null)
        return 0;
    int[][] dp = new int[word1.length() + 1][word2.length() + 1];// 定义Dp数组以及初始化
    for (int i = 0; i <= word1.length(); i++) {
        dp[i][0] = i;
    }
    for (int j = 0; j <= word2.length(); j++) {
        dp[0][j] = j;
    }
    for (int i = 1; i <= word1.length(); i++) {
        for (int j = 1; j <= word2.length(); j++) {
            if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
                dp[i][j] = dp[i - 1][j - 1];
            } else {
                dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1);
            }
        }
    }
    return dp[word1.length()][word2.length()];

}

72.编辑距离

力扣题目链接(opens new window)

给你两个单词 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 由小写英文字母组成

思路

思路:动态规划
动态规划五部曲
1.定义dp数组以及下标含义
定义二维dp数组。dp[i][j]表示以字符word1[i-1]为结尾字符串和以字符word2 [j-1]为结尾字符串中,word1 转换成 word2 所使用的最少操作数 。
2.定义递推公式
word1[i-1] == word2 [j-1].dp[i][j] = dp[i-1][j-1]
word1[i-1] != word2 [j-1].对word1有增删改三种操作
word1删除操作 dp[i][j] = dp[i-1][j] + 1;
word1增加操作 dp[i][j] = dp[i + 1][j] + 1 = dp[i][j - 1] + 1;
word1修改操作 dp[i][j] = dp[i - 1][j - 1] + 1;
这里解释下word1增加操作递推公式怎么推导
当word1字符串尾部新增元素后,长度由i 变为 i + 1.那么dp[i][j] = dp[i + 1][j] + 1.但如果这么写的话,dp[i][j]就要先得到dp[i+1]的值才能推导出来
很显然和删除修改操作的dp公式存在冲突,这两个公式都是先得到dp[i - 1]的值后推导dp[i][j]。需要把dp[i + 1][j] 转换成跟 i-1有关的dp公式
word1添加一个元素,相当于word2删除一个元素,例如 word1 = "a" ,word2 = "ad",word1添加元素'd' 和 word2删除一个元素'd',变成word1="ad", word2="a", 最终的操作数是一样!
所以word1增加操作 dp[i][j] = dp[i + 1][j] + 1 = dp[i][j - 1] + 1;
3.初始化dp数组
dp[i][0] 和 dp[0][j] 表示什么呢?
dp[i][0] :以下标i-1为结尾的字符串word1,和空字符串word2,最近编辑距离为dp[i][0]。
那么dp[i][0]就应该是i,对word1里的元素全部做删除操作,即:dp[i][0] = i;
同理dp[0][j] = j;
4.遍历顺序
有递推公式可知
从小到大遍历即可
5.举例推导dp数组

代码如下

// 时间复杂度O(N^2)
// 空间复杂度O(N^2)
public int minDistance(String word1, String word2) {
    int[][] dp = new int[word1.length() + 1][word2.length() + 1];
    for (int i = 0; i <= word1.length(); i++) {
        dp[i][0] = i;
    }
    for (int j = 0; j <= word2.length(); j++) {
        dp[0][j] = j;
    }


    for (int i = 1; i <= word1.length(); i++) {
        for (int j = 1; j <= word2.length(); j++) {
            if (word1.charAt(i-1) == word2.charAt(j-1))
                dp[i][j] = dp[i - 1][j - 1];
            else {
                dp[i][j] = Math.min(dp[i][j - 1], dp[i - 1][j]) + 1;
            }
        }
    }
    return dp[word1.length()][word2.length()];

}