leetcode-72. 编辑距离
求解方法
知道了编辑距离的定义,那么如何求最小编辑距离呢?这里用到了动态规划的思想。
用例子来说明,假如我们要求解 jary和jerry的最小编辑距离,那么首先要创建如下矩阵:
Φ | j | a | r | y | |
Φ | 0 | 1 | 2 | 3 | 4 |
j | 1 | ||||
e | 2 | ||||
r | 3 | ||||
r | 4 | ||||
y | 5 |
这个矩阵什么意思呢?第一行是字符串jary,第一列是字符串jerry,每个标有数字的单元格代表了两个字符串的子串的最小编辑距离。第二行第二列的0代表两个字符串都取空子串的时候,编辑距离就是0(子串相等);第二行第三列的1代表当jerry的子串取空,jary的子串取j时,两个子串的最小编辑距离是1(给jerry的子串插入j)。其他的依次类推,可以很容易得出当前矩阵中的第二行和第二列的数字。
而我们最终要求的两个字符串的最小编辑距离对应的就是矩阵右下角的那个单元格,它代表当jary子串取jary,jerry子串取jerry时,两个子串的最小编辑距离,也就是两个字符串的最小编辑距离。
这里我先直接说怎么求,然后再解释原理。看下面的矩阵,我在中心空白的位置标上了从x1到x20,这里x后面的数字代表我们求解时的顺序。
j | a | r | y | ||
0 | 1 | 2 | 3 | 4 | |
j | 1 | x1 | x6 | x11 | x16 |
e | 2 | x2 | x7 | x12 | x17 |
r | 3 | x3 | x8 | x13 | x18 |
r | 4 | x4 | x9 | x14 | x19 |
y | 5 | x5 | x10 | x15 | x20 |
如果按顺序求解的话,那么在求解每一个值的时候,它的左、上、左上三个位置的单元格值肯定都是已知的,将这三个单元格里的值分别定义为left、top、leftTop,则要求解的单元格的值v为:
若单元格横向对应的字符和纵向对应的字符相等则为cost为0
如果不等:
cost = min(left+1,top+1,leftTop+1)
因此下面按照动态规划进行求解:
dp(i, j) 函数的定义是这样的:
def dp(i, j) 表示 s1[0..i] 和 s2[0..j] 的最小编辑距离
base case:
dp[..][0]
和 dp[0][..]
对应 base case
状态转移方程:
if s1[i] == s2[j]:
return dp(i - 1, j - 1) # 啥都不做
else:
return min(
dp(i, j - 1) + 1, # 插入
dp(i - 1, j) + 1, # 删除
dp(i - 1, j - 1) + 1 # 替换
)
class Solution {
public:
int minDistance(string word1, string word2) {
int m = word1.length();
int n = word2.length();
int dp[m+1][n+1];
//base case
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];
}
};