每日一道leetcode(这题太难了,想了一晚上没想出来)

72. 编辑距离 - 力扣(LeetCode)

题目

给你两个单词 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. 每一次比较后我们可以采取的策略有四种:插入、删除、替换、跳过。
  2. 因为从一个字符串变成另一个字符串,如果两者长度不一样,那么中间的差值必然会变成插入或者删除的操作数,我们是否可以先利用某种方法先转换成长度一致的两个字符串,再只考虑替换的问题呢?
  3. 第二,删除、替换、插入、跳过的状态变化都不一样,有没有什么办法降维或者变换使得状态变化趋于一致?

代码实现

复杂度分析

官方题解

  • 看官解吧,我感觉官解都得理解一会儿才行。
  • 其实最重要的就是理解每个状态该怎么定义,我把每个状态看成了单个字符的比较,没有考虑到比较到当前字符时前驱节点已经是局部最优的情况,所以后续的状态转移就非常的难受。
  • 第二就是这里的局部最优的叠加就是全局最优,我一直没办法证明,当时脑子其实一动想用归纳证明一下的,但是又倔强说这玩意儿就是逆天而行,不顺着推怎么锻炼思维,然后就整死自己了qwq。

思路

  1. 首先是降维问题,如果把字符串A的删除操作替换为B的插入操作,那么操作类型就会直接减少。
  2. 这里面还有一个很重要的点,就是无论word1[i]和word2[j]是两堆什么样的字符串,如果前序已经完成变换了,那么如果i和j不一样,那么他们必然要发生一次操作。
  3. 然后就是状态的表示。
    1. 首先引入一个概念(其实就是题目的解释)——编辑距离——一个字符串转换成为另外一个字符串所需要的最小操作数。
    2. dp[i][j]定义A的前i个字符(即前i-1位)转换成B的前j个(前j-1位)字符所需要的编辑距离。
    3. 那么到达dp[i][j]可能有三种情况:
      1. dp[i-1][j]已经匹配,那么只要删除第i位即可——删除;
      2. dp[i][j-1]已经匹配,那么只要删除第j位(等价于在A添加一个和B的第j位匹配的字符)——插入;
      3. dp[i-1][j-1]后,说明到达i-1,j-1位时,前i-2,j-2位的内容已经匹配好了,若A[i-1]==B[i-1]就直接保持,若不等就替换其中一个。
    4. 那么就可以得出状态转移方程:
      1. dp[i][j] = min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]+int(word1[i-1]!=word2[j-1]));
    5. 最后需要考虑初始值和边界值:
      1. 当字符串为空的时候,编辑距离即是双方的长度和。
      2. dp[i]][0],dp[0][j]分别都为i和j。

代码复现

class Solution {
public:
    int minDistance(string word1, string word2) {
        int n1 = word1.length(), n2 = word2.length();
        if(n1*n2 == 0) return n1+n2;
        int dp[n1+1][n2+1];
        for(int i = 0; i <= n1; i++) dp[i][0] = i;
        for(int j = 0; j <= n2; j++) dp[0][j] = j;
        for(int i = 1; i <= n1; i++) {
            for(int j = 1; j <= n2; j++) {
                dp[i][j] = min(dp[i-1][j]+1, min(dp[i][j-1]+1, dp[i-1][j-1]+int(word1[i-1]!=word2[j-1])));
            }
        }
        return dp[n1][n2];
    }
};

复杂度分析

  • 时间复杂度:O(n1*n2)。
  • 空间复杂度:O(n1*n2)。

进阶:

  • 因为每次状态更新都只涉及上一层的结果,所以其实可以降维成一维的动态规划,只要额外把重复的j-1部分保存即可。
  • 那么空间复杂度就可以降维成O(n2)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值