【Leetcode】72. Edit Distance(配数学证明)

题目地址:

https://leetcode.com/problems/edit-distance/

给定两个字符串 s s s t t t,对 s s s可以每次进行三种操作其中的一个:
1、将 s s s中新加一个字符;
2、将 s s s中删除一个字符;
3、将 s s s中替换一个字符。
问最少对 s s s进行多少次操作可以将其变为 t t t

思路是动态规划。先考虑极端情况,如果 s s s为空串,那么很显然必须也只能向 s s s中添加 t t t中所有字符才可以,也就是进行 l t l_t lt这么多次;如果 t t t为空串,那么就是从 s s s中删掉所有字符,也就是进行 l s l_s ls这么多次。(严格证明是这样的,因为三个变换中只有insert和delete是会改变字符串长度的,所以要将 s s s变为空串的最短路径必然是每一步都将长度减少 1 1 1,所以最少操作次数必然是 s s s的长度)。

接下来考虑一般情况,设 f [ i ] [ j ] f[i][j] f[i][j]为将 s s s中前 i i i个字符(从 0 0 0开始计数,前 0 0 0个字符视为空串)变为 t t t中前 j j j个字符最少需要多少次变换。考虑 f [ i ] [ j ] f[i][j] f[i][j]的递推关系:
1、若 s [ i − 1 ] ≠ t [ j − 1 ] s[i-1]\ne t[j-1] s[i1]=t[j1],令 a = s [ : i − 1 ] a=s[:i-1] a=s[:i1] b = t [ : j − 1 ] b=t[:j-1] b=t[:j1],考虑 a a a变为 b b b的过程。设 a → a 1 → a 2 → . . . → a k → a k + 1 → . . . → b a\to a_1\to a_2\to ...\to a_k\to a_{k+1}\to ...\to b aa1a2...akak+1...b,其中 a k + 1 a_{k+1} ak+1以及之后的所有字符串末尾都是 s [ i − 1 ] s[i-1] s[i1],那么可以考虑从 a k → a k + 1 a_k\to a_{k+1} akak+1是执行了什么操作。如果其是添加,那么可以将这一步添加挪到最后一步做,可以产生一个等价的变换操作(还有很多细节在里面,试讨论之。如果 a k + 1 a_{k+1} ak+1之后的操作不是对最后一个字符的操作,或者可以等效替换为不是对最后一个字符的操作,就可以证明这个结论了。如果其是替换最后一个字符,那是多此一举,不可能;如果是添加和删除,则都可以等效替换,例如最后添加 s [ i − 1 ] s[i-1] s[i1],那么可以相当于是对除了末尾的前面一段添加这个字符,确实可以等效替换的。删除也类似),等价替换之后步数一样,都大于等于 f [ i ] [ j − 1 ] + 1 f[i][j-1]+1 f[i][j1]+1;如果 a k → a k + 1 a_k\to a_{k+1} akak+1是执行了删除或者替换,那么也可以类似证明,可以放在最后做。所以此时 f [ i ] [ j ] = min ⁡ { f [ i ] [ j − 1 ] + 1 , f [ i − 1 ] [ j − 1 ] + 1 , f [ i − 1 ] [ j ] + 1 } = 1 + min ⁡ { f [ i ] [ j − 1 ] , f [ i − 1 ] [ j − 1 ] , f [ i − 1 ] [ j ] } f[i][j]=\min\{f[i][j-1]+1,f[i-1][j-1]+1,f[i-1][j]+1\}=1+\min\{f[i][j-1],f[i-1][j-1],f[i-1][j]\} f[i][j]=min{f[i][j1]+1,f[i1][j1]+1,f[i1][j]+1}=1+min{f[i][j1],f[i1][j1],f[i1][j]}
2、若 s [ i − 1 ] = t [ j − 1 ] s[i-1]=t[j-1] s[i1]=t[j1],则说明 s s s t t t最后一个字符相等,那就直接变换前面的字符即可,所以 f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] f[i][j]=f[i-1][j-1] f[i][j]=f[i1][j1]。其实我们还需要证明 f [ i − 1 ] [ j − 1 ] ≤ 1 + min ⁡ { f [ i ] [ j − 1 ] , f [ i − 1 ] [ j ] } f[i-1][j-1]\le 1+\min\{f[i][j-1],f[i-1][j]\} f[i1][j1]1+min{f[i][j1],f[i1][j]}。先证 f [ i − 1 ] [ j − 1 ] ≤ 1 + f [ i ] [ j − 1 ] f[i-1][j-1]\le 1+f[i][j-1] f[i1][j1]1+f[i][j1]。设 a ′ a' a b ′ b' b分别是 a a a b b b去掉最后一个字符得到的字符串。那么 f [ i − 1 ] [ j − 1 ] f[i-1][j-1] f[i1][j1]对应的方案其实就是 a ′ a' a变成 b ′ b' b的方案。考虑这样一种方案,先将 a ′ a' a后面添加 s [ i − 1 ] s[i-1] s[i1],然后执行 f [ i ] [ j − 1 ] f[i][j-1] f[i][j1]次操作变为 b ′ b' b,步数为 1 + f [ i ] [ j − 1 ] 1+f[i][j-1] 1+f[i][j1],而从 a ′ → b ′ a'\to b' ab的最快方案需要 f [ i − 1 ] [ j − 1 ] f[i-1][j-1] f[i1][j1]步,所以有 f [ i − 1 ] [ j − 1 ] ≤ 1 + f [ i ] [ j − 1 ] f[i-1][j-1]\le 1+f[i][j-1] f[i1][j1]1+f[i][j1];再证 f [ i − 1 ] [ j − 1 ] ≤ 1 + f [ i − 1 ] [ j ] f[i-1][j-1]\le 1+f[i-1][j] f[i1][j1]1+f[i1][j],与刚才类似,考虑这样一种方案,先将 a ′ a' a变为 b ′ + s [ i − 1 ] b'+s[i-1] b+s[i1],然后后面删除 s [ i − 1 ] s[i-1] s[i1],其需要 1 + f [ i − 1 ] [ j ] 1+f[i-1][j] 1+f[i1][j]步,所以有 f [ i − 1 ] [ j − 1 ] ≤ 1 + f [ i ] [ j − 1 ] f[i-1][j-1]\le 1+f[i][j-1] f[i1][j1]1+f[i][j1]

综上所述,代码如下:

class Solution {
 public:
  int minDistance(string s1, string s2) {
    int m = s1.size(), n = s2.size();
    int f[m + 1][n + 1];
    for (int i = 0; i <= m; i++)
      for (int j = 0; j <= n; j++) {
        if (!i || !j) f[i][j] = i ^ j;
        else {
          if (s1[i - 1] == s2[j - 1]) f[i][j] = f[i - 1][j - 1];
          else {
            f[i][j] = min(f[i][j - 1] + 1, f[i - 1][j - 1] + 1);
            f[i][j] = min(f[i][j], 1 + f[i - 1][j]);
          }
        }
      }

    return f[m][n];
  }
};

时空复杂度 O ( m n ) O(mn) O(mn) m m m n n n分别为两个字符串长度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值