解题思路:
这道题很经典的dp问题。
首先我们只有三种操作:
- 删除一个字符
- 添加一个字符
- 修改一个字符
我们需要用最少的步数将word1转成word2。
我们可以发现以下规律:
- 从word1变成word2不可能是先删掉一个字符,再把这个字符加回来,这样平白无故的多出了两次操作,肯定不是最少步数
- 我们操作的顺序并不影响结果,比如abcd编程ace,需要删除bd添加e,这三步我们先做哪一步结果都是一样的
dp的要素:
考虑集合的表示:
f[i][j], 用这个二维数组表示。表示word1的前i个字符编程word2的前j个字符的所有操作方法。值代表了需要的最少步数。
考虑状态计算(状态转移方程):
由第二个规律我们从前往后做(从前往后扫描字符串改变)。
如果我们想要word1的前i个字符和word2的前j个字符相同,因为是从前往后做的,所以有以下几种情况。
- word1添加一个字符和word2一样。这需要word1的前i个和word2的前j-1个相同。即f[i][j] = f[i][j-1] + 1。这个地方是因为如果word1的前i个和word2的前j-1个相同,那只需要word1添加一个字符和word2[j]相同即可。
- word1删除一个字符和word2一样。这需要word1的前i-1个和word2的前j个相同。即f[i][j] = f[i-1][j] + 1
- word1修改一个字符和word2一样。需要保证word1的前i-1个和word2的前j-1个相同。然后判断是否word1[i]=word2[j]。即f[i][j] = f[i-1][j-1] + t。如果word1[i]=word2[j],t = 0,否则t = 1。
有的写还有三种,那三种是word2向word1变,状态方程和上面三个分别对应。这里不写。
代码实现:
class Solution {
public int minDistance(String word1, String word2) {
int len1 = word1.length(), len2 = word2.length();
// dp问题从1走比较好,不用考虑边界,所有加一个空格
word1 = " " + word1;
word2 = " " + word2;
int[][] f = new int[len1 + 5][len2 + 5];
// 合法的初始化,一个单词变成另一个时
// 如果从当前变成对方的前0个字符,那只能一直删除
// 所以有多少个字符最少距离就是几
for (int i = 0; i <= len1; i ++ ) f[i][0] = i;
for (int i = 0; i <= len2; i ++ ) f[0][i] = i;
for (int i = 1; i <= len1; i ++ )
for (int j = 1; j <= len2; j ++ ) {
// 前两种情况
f[i][j] = Math.min(f[i - 1][j], f[i][j - 1]) + 1;
int t = 0;
if(word1.charAt(i) != word2.charAt(j)) t = 1;
// 第三种情况
f[i][j] = Math.min(f[i][j], f[i - 1][j - 1] + t);
}
return f[len1][len2];
}
}