LeetCode系列72—编辑距离

题意

给你两个单词 word1word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。

你可以对一个单词进行如下三种操作:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符

题解

解决两个字符串的动态规划问题,一般都是用两个指针 ij 分别指向两个字符串的最后,然后一步步往前走,缩小问题的规模

假设字符串分别为 str1str2,且 str1 变为 str2, 每一步有4种选择:

如果str1[i] == str[j],那么不需要任何操作,ij 都前移即可

否则,可以进行插入删除,或者替换,具体选择那个需要全试一遍

如果是插入,j前移,因为 str1插入了 j对应的元素,j 已经完成比较,而 i 还没有比较

同理可得到删除和替换的状态转移方程

用递归的思想不做任何优化是这样实现的

int dfs(int i, int j)
{
	if(i == -1) return j+1;
	if(j == -1) return i+1;
	if(str1[i] == str2[j])
		return dfs(i-1, j-1)
	else{
		return min(
				dfs(i, j-1)+1,   //插入
				dfs(i-1, j)+1,   //删除
				dfs(i-1, j-1)+1  //替换
		}
	}
}

显而易见,这会导致很多重复的操作,所以可以用备忘录来优化,备忘录完整的代码如下:

class Solution {
private:
    string word1;
    string word2;
public:
    int minDistance(string word1, string word2) {
        this->word1 = word1;
        this->word2 = word2;
        int len1= word1.size();
        int len2= word2.size();
        int i = len1 - 1;
        int j = len2 - 1;
        vector<vector<int>> memo(len1, vector<int>(len2, -1));
        return dfs(i,j, memo);
    }

    int dfs(int i, int j, vector<vector<int>>& memo){
        if(i == -1) return j+1;
        if(j == -1) return i+1;
        if(memo[i][j] != -1)
            return memo[i][j];
        if(word1[i] == word2[j])
            memo[i][j] = dfs(i-1, j-1, memo);
        else
            memo[i][j] = min(dfs(i-1,j-1, memo), min(dfs(i-1,j, memo), dfs(i, j-1, memo))) + 1;
        return memo[i][j];
    }

};

当然,此题正规的做法还是动态规划

dp[i][j] 表示 str1[0..i]str2[0..j] 的最小编辑距离,状态转移方程与递归方法类似,base case注意,因为数组的索引不能是-1,所以 dp 数组会偏移一位, dp[..][0]dp[0][..]对应base case,代码如下:

class Solution {
public:
    int minDistance(string word1, string word2) {
        int m = word1.size();
        int n = word2.size();
        int dp[m+1][n+1];
        for(int i = 0; i <= m; i++)
            dp[i][0] = i;
        for(int j = 0; j <= n; j++)
            dp[0][j] = j;
        
        for(int i = 1; i <= m; i++){
            for(int j = 1;j <= n; j++){
                if(word1[i-1] == word2[j-1])
                    dp[i][j] = dp[i-1][j-1];
                else
                    dp[i][j] = min(dp[i-1][j]+1, min(dp[i][j-1]+1, dp[i-1][j-1]+1));
            }
        }
        return dp[m][n];
    }
};

有一个细节,既然每个 dp[i][j] 只和它附近的三个状态有关,空间复杂度是可以压缩成 O ( m i n ( M , N ) ) O(min(M, N)) O(min(M,N))的(M,N是两个字符串的长度),但是压缩后代码的可解释性大大降低,没有意义

还有一个问题,这里只求出了最小的编辑距离,但是具体的操作并不清楚

通过给 dp 数组增加额外的信息即可

class Node{
	int val;
	int choice;
	// 0代表跳过,1代表插入,2代表删除,3代表替换
}
Node dp[][];

通过记录的 choice 向前寻找即可

参考

动态规划:编辑距离

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值