leetcode 72.编辑距离

编辑距离可以说是动态规划算法中经典的、知名的题目了,题目难度也不小,是一道很好的动态规划的题目。很可能会出现在面试中动态规划的考察上。

题目

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

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

  1. 插入一个字符
  2. 删除一个字符
  3. 替换一个字符

示例 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’)

题目求解

在动态规划题目的求解中,首先要明确数组元素的含义,对于本题:
dp[i][j] 表示将 word1 的前 i 个字符转换成 word2 的前 j 个字符所需要的最少操作数。

接着就是写出状态转移方程,动态规划题目的难度或者说复杂度往往是由状态转移方程的复杂度决定的。
对于本题:
dp[i][j] = word1[i] == word2[j] ? dp[i-1][j-1] : min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1])+1;

或者展开成下面的形式,可能更加直观:

if(word1[i] == word2[j])
	dp[i][j] = dp[i-1][j-1];
else
	dp[i][j] = min(
		dp[i][j-1]+1, // 对应插入字符操作
		dp[i-1][j]+1, // 对应删除字符操作
		dp[i-1][j-1]+1 // 对应替换字符操作
	);

其中,

if(word1[i] == word2[j])
	dp[i][j] = dp[i-1][j-1];

的含义是,如果 word1 的第 i 个字符和 word2 的第 j 个字符相等,那么将 word1 的前 i 个字符转换成 word2 的前 j 个字符所需要的最少操作就等于将 word1 的前 i-1 个字符转换成 word2 的前 j-1 个字符所需要的最少操作。

其中,

if(word1[i] != word2[j]){
	dp[i][j] = min(
		dp[i][j-1]+1, // 对应插入字符操作
		dp[i-1][j]+1, // 对应删除字符操作
		dp[i-1][j-1]+1 // 对应替换字符操作
	);
}

先说插入字符操作,dp[i][j] = dp[i][j-1]+1,对于这个式子你可能会产生这样的疑问?dp[i][j-1] 表示的是将 word1 前 i 个字符转换成 word2 前 j-1 个字符,那现在在 word1 中插入一个等于 word2[j] 的字符,表示的应该是 dp[i+1][j] 的含义呀。那是因为你插入的这个新字符并不属于原生的 word1,这还得回到 dp[i][j] 的定义上:它表示的是将 word1 的前 i 个字符转换成 word2 的前 j 个字符所需要的最少操作数。

而删除操作就不一样了,它删除的就是 word1 中的原生字符(不用考虑删除的是之前插入字符的情况,这是一对冗余无效的操作,不会出现在 dp 所表示的最少操作中),所以它对应的是 dp[i-1][j]+1,同理,替换操作替换的也是 word1 中的原生字符。

参考代码:

class Solution {
    public int minDistance(String word1, String word2) {
        int[][] dp = new int[word1.length()+1][word2.length()+1];
        for(int i = 0; i < dp.length; ++i) dp[i][0] = i;
        for(int i = 0; i < dp[0].length; ++i) dp[0][i] = i;
        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] = min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1]) + 1;
            }
        }
        return dp[word1.length()][word2.length()];
    }
    private int min(int a, int b, int c){
        if(a > b) a = b;
        return a < c ? a : c;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章