例题: 编辑距离
如果没有接触过动态规划的话,这道题还是比较有难度的。了解过动态规划之后,按照套路进行分析即可:
(1) 定义dp数组
用dp[i][j]表示word1中以i-1为结尾的子串和word2中以j-1为结尾的子串的最小编辑距离。
这里之所以表示-1,只是为了初始化时的方便。
(2) 确定递推关系
确定递推关系时,需要分情况讨论,不失一般性,我们考虑将word1编辑为word2:
当word1[i-1] == word2[j-1]时,说明当前字符不需要进行编辑,即dp[i][j] = dp[i-1][j-1];
当word1[i-1] != word2[j-1]时,说明需要进行编辑,有三种编辑方式:
- 删除word1[i-1],即
dp[i][j] = dp[i-1][j] + 1; - 给word1最后追加上word2[j-1],这样最后一位就相同了,只需要考虑前边的了,即
dp[i][j] = dp[i][j-1] + 1;(或者理解为删除word2[j-1],因为两种方式所需的操作数一致) - 将word1[i-1]修改为word2[j-1],即
dp[i][j] = dp[i-1][j-1] + 1;
这四种情况中的最小值就是最小编辑距离。
(3) 初始化
根据dp的定义,dp[i][0]表示将word1中以i-1为结尾的子串变为空串的编辑次数,很明显是i次,所以dp[i][0] = i;同理,dp[0][j] = j。
(4) 确定递推顺序
从递推关系可以看出,dp[i][j]由dp[i-1][j-1]、dp[i-1][j]以及dp[i][j-1]得到,也就是从左上、上以及左进行递推,因此顺序为从上往下,从左往右。
public int minDistance(String word1, String word2) {
// dp[i][j]表示word1中以i-1为结尾的子串和word2中以j-1为结尾的子串的最小编辑距离
int[][] dp = new int[word1.length() + 1][word2.length() + 1];
// 初始化
for (int i = 0; i <= word1.length(); i++) {
dp[i][0] = i;
}
for (int j = 0; j <= word2.length(); j++) {
dp[0][j] = j;
}
for (int i = 1; i <= word1.length(); i++) {
for (int j = 1; j <= word2.length(); j++) {
char c1 = word1.charAt(i - 1);
char c2 = word2.charAt(j - 1);
if (c1 == c2) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
}
}
}
return dp[word1.length()][word2.length()];
}