题目来源:https://leetcode.com/problems/edit-distance/
问题描述
72. Edit Distance
Hard
Given two words word1 and word2, find the minimum number of operations required to convert word1 to word2.
You have the following 3 operations permitted on a word:
- Insert a character
- Delete a character
- Replace a character
Example 1:
Input: word1 = "horse", word2 = "ros"
Output: 3
Explanation:
horse -> rorse (replace 'h' with 'r')
rorse -> rose (remove 'r')
rose -> ros (remove 'e')
Example 2:
Input: word1 = "intention", word2 = "execution"
Output: 5
Explanation:
intention -> inention (remove 't')
inention -> enention (replace 'i' with 'e')
enention -> exention (replace 'n' with 'x')
exention -> exection (replace 'n' with 'c')
exection -> execution (insert 'u')
------------------------------------------------------------
题意
两个字符串的编辑距离:给定两个字符串s1, s2,定义三种操作:增(一个字符)、删(一个字符)、改(一个字符变另一字符),求从s1变到s2最少需要的操作次数?
------------------------------------------------------------
思路
注意到问题具有最优子结构(如果s1和s2的编辑距离是最小的,那么s1的子串和s2的子串,按照s1到s2的编辑方式,其编辑距离也是最小的),故可以用动态规划求解:dp[i][j]表示s1[0:i]到s2[0:j]的编辑距离。转移方程:
dp[i][j] = dp[i-1][j-1], if s1[i-1] == s2[j-1] ********** (1)
= min(dp[i-1][j-1], dp[i][j-1], dp[i-1][j]) + 1, if s1[i-1] != s2[j-1] ********** (2)
(注意到“增”“删”是等价的,增s1就等价于删s2)
其实每次状态转移就是在“增”“删”“改”(“增删”对应dp[i-1][j]和dp[i][j-1],“改”对应dp[i-1][j-1])三种方式中选一个操作次数最少的。而s1[i-1] == s2[j-1]的时候只用考虑dp[i-1][j-1]不用考虑dp[i-1][j]或dp[i][j-1]是因为如下不等式组:
dp[i-1][j-1] <= dp[i][j] (最优子结构) <= dp[i-1][j] + 1 (或dp[i][j-1] + 1) (由式(2)可知)
这个不等式组保证了
dp[i-1][j-1] <= dp[i-1][j] + 1且dp[i][j-1] + 1
但是注意它并不保证
dp[i-1][j-1] + 1<= dp[i-1][j] + 1
所以式(2)中不能把dp[i-1][j-1]精简掉。
------------------------------------------------------------
代码
class Solution {
public int minDistance(String word1, String word2) {
int n1 = word1.length(), n2 = word2.length();
int[][] dp = new int[n1+1][n2+1];
if (n1 == 0)
{
return n2;
}
if (n2 == 0)
{
return n1;
}
dp[0][0] = 0;
int i = 0, j = 0;
for (i=1; i<=n1; i++)
{
dp[i][0] = i;
}
for (i=1; i<=n2; i++)
{
dp[0][i] = i;
}
for (i=1; i<=n1; i++)
{
for (j=1; j<=n2; 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-1][j-1], dp[i-1][j], dp[i][j-1]) + 1
dp[i][j] = Math.min(dp[i][j-1], dp[i-1][j]);
dp[i][j] = Math.min(dp[i][j], dp[i-1][j-1]);
dp[i][j]++;
}
}
}
return dp[n1][n2];
}
}