代码随想录算法训练营第五十六天| 583. 两个字符串的删除操作 ,72. 编辑距离,编辑距离总结篇

题目与题解

参考资料:编辑距离总结篇

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

题目链接:583. 两个字符串的删除操作

代码随想录题解:​​​​​​​583. 两个字符串的删除操作

视频讲解:动态规划之子序列,还是为了编辑距离做铺垫 | LeetCode:583.两个字符串的删除操作_哔哩哔哩_bilibili

解题思路:

        这道题跟求最长公共子序很像,因为操作步骤要最少,意味着两个字符串删除剩下的字符要尽可能多,删除后剩下的序列又是一样的,其实最后剩下的必然是公共子序。所以只要求出最长公共子序的长度,再分别用两个字符串的长度减去公共长度,得到的两个字符串的长度和就是删除的字符长度,即所需答案。

class Solution {
    public int minDistance(String word1, String word2) {
		int[][] dp = new int[word1.length()+1][word2.length()+1];
		int maxCommonLen = 0;
		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]+1;
				} else {
					dp[i][j] = Math.max(dp[i][j-1], dp[i-1][j]);
				}
				maxCommonLen = Math.max(maxCommonLen, dp[i][j]);
			}
		}
		return word1.length() + word2.length() - 2*maxCommonLen;
    }
}

看完代码随想录之后的想法 

        求最长公共子序有一点逆向思维。随想录给出了正向的解题方式。

        递推公式主要着眼于每一步删除哪一个字符串的字符,分两种情况讨论

  • 当word1[i - 1] 与 word2[j - 1]相同的时候
  • 当word1[i - 1] 与 word2[j - 1]不相同的时候

当word1[i - 1] 与 word2[j - 1]相同的时候,dp[i][j] = dp[i - 1][j - 1];

当word1[i - 1] 与 word2[j - 1]不相同的时候,有三种情况:

情况一:删word1[i - 1],最少操作次数为dp[i - 1][j] + 1,表示删除word1的字符

情况二:删word2[j - 1],最少操作次数为dp[i][j - 1] + 1,表示删除word2的字符

情况三:同时删word1[i - 1]和word2[j - 1],操作的最少次数为dp[i - 1][j - 1] + 2

因为 dp[i][j - 1] + 1 = dp[i - 1][j - 1] + 2,所以递推公式可简化为:dp[i][j] = min(dp[i - 1][j] + 1, dp[i][j - 1] + 1); 从字面上理解 就是 当 同时删word1[i - 1]和word2[j - 1],dp[i][j-1] 本来就不考虑 word2[j - 1]了,那么在删 word1[i - 1]时,就达到两个元素都删除的效果,即 dp[i][j-1] + 1。

考虑到有删除的情况,所以对于下标为0的dp一定要初始化,即空字串相对有数值的字符串必须有操作。

dp[i][0]:word2为空字符串,以i-1为结尾的字符串word1要删除多少个元素,才能和word2相同呢,很明显dp[i][0] = i。dp[0][j]的话同理。

考虑递推来源,遍历顺序是从上到下,从左到右。

// dp数组中存储需要删除的字符个数
class Solution {
    public int minDistance(String word1, String word2) {
        int[][] dp = new int[word1.length() + 1][word2.length() + 1];
        for (int i = 0; i < word1.length() + 1; i++) dp[i][0] = i;
        for (int j = 0; j < word2.length() + 1; j++) dp[0][j] = j;
        
        for (int i = 1; i < word1.length() + 1; i++) {
            for (int j = 1; j < word2.length() + 1; 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] + 2,
                                        Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1));
                }
            }
        }
        
        return dp[word1.length()][word2.length()];
    }
}

遇到的困难

        正向的有点难想,所以直接利用最长公共子序的反向思维了。正向的也要掌握。

72. 编辑距离

题目链接:​​​​​​​72. 编辑距离

代码随想录题解:72. 编辑距离

视频讲解:动态规划终极绝杀! LeetCode:72.编辑距离_哔哩哔哩_bilibili

解题思路:

        dp表示对当前以word1[i-1]为结尾的子字符串要转变成word2[j-1]为下标的子字符串,最少需要多少步。

        递推公式同样分两种情况:

        word1[i-1] = word2[j-1]时:不需要任何增删改操作,此时dp[i][j] = dp[i-1][j-1]

        二者不相等时:如果是删除操作,跟上一题一样,dp[i][j] = dp[i-1][j] + 1

        增和改就有点犯难了,看答案。

看完代码随想录之后的想法 

        增加元素操作:word1要增加一个元素,可以相当于word2删除一个元素的操作总数,所以可以简单写成dp[i][j] = dp[i][j-1] + 1

        替换元素:word1[i-1] = word2[j-1]时,dp[i][j] = dp[i-1][j-1],那么要通过替换元素使word1[i-1] = word2[j-1],只需要在dp[i-1][j-1]的基础上增加一次替换操作即可,即dp[i][j] = dp[i-1][j-1]+1。

        所以最终的dp[i][j]就是增删改这三个递推公式中的最小值。

        初始化参考上一题正向思维的初始化方式,即空字符串要通过操作变成另一个字符串,或一个字符串要通过操作变成空字符串,需要初始化0行0列为非空字符串当前的长度。

        顺序同样是按从上到下,从左到右操作。

class Solution {
    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-1][j-1] + 1,
							Math.min(dp[i-1][j] + 1, dp[i][j-1] + 1));
				}
			}
		}
		return dp[word1.length()][word2.length()];
    }
}

遇到的困难

        删和改的递推公式不大会,记住吧。

今日收获

        学习了一系列两个序列相关关系的动态规划题,其本质思路为:

        dp定义:考虑到有空数组或空串,为了初始化方便,dp一般比输入的数组多取一个长度,定义为以数组1[i-1]为结尾的子数组和数组2[j-1]的子数组的关系,该关系为题目要求的最终结果。

        递推公式:一般分为两种情况:当前数组对应的值相同,得到一个递推,不相同时得到另一个,根据题目需求进行增减值

        初始化:根据情况,全部初始化为0,1,或者对应的当前数组长度

        方向:一般由左上角推到右下角,所以从上到下,从左到右遍历

        后面二刷可以再熟悉一下。

        

  • 8
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值