leetcode583题目: 给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。
输入: “sea”, “eat”
输出: 2
解释: 第一步将"sea"变为"ea",第二步将"eat"变为"ea"
在用动态规划算法做了最长公共子序列的题目后,发现此题也可用类似解法来做。
最长公共子序列的做法,可以学习这篇leetcode题解,写的非常好。
主要是这个图片非常的形象(摘自leetcode,刚上面链接),如下:
按照这个思路来解,做法如下:
def minDistance(self, word1: str, word2: str) -> int:
m = len(word1)
n = len(word2)
# 由于在处理前每个字符前加入一个空字符,方便处理,故dp的长度是m + 1行 和 n + 1列
# 注意,前面的循环是列数,后面的是行数
dp = [[0 for i in range(n + 1)] for j in range(m + 1)] # m + 1 行 n + 1 列
# base case
# 可以想到,如果其中一个字符串为空,那么需要步数就是另外一个字符的长度了
# 由此,可以得到基准条件,用来递推其他情况
for i in range(m + 1):
for j in range(n + 1):
if i == 0 or j == 0:
dp[i][j] = i + j
for i in range(m):
for j in range(n):
if word1[i] == word2[j]:
# 如果当前字符相等,即说明当前字符不需要处理。 那么dp[i + 1][j + 1] = dp[i][j],即s1,s2都退一格的情况。
dp[i + 1][j + 1] = dp[i][j]
else:
# 如果不等,则取s1退一格的情况与s2退一格的情况取最小值,再加上1,表示删除当前不一致的字符的步数。
dp[i + 1][j + 1] = min(dp[i][j + 1], dp[i + 1][j]) + 1
return dp[m][n]
官方给出的其中一种解答如下,主要是对于索引的处理不一致,思路是一样的。 不过对于基准条件的计算可以放到和递推循环一起。
public int minDistance(String s1, String s2) {
int[][] dp = new int[s1.length() + 1][s2.length() + 1];
for (int i = 0; i <= s1.length(); i++) {
for (int j = 0; j <= s2.length(); j++) {
// 将基准条件放在这里赋值
if (i == 0 || j == 0)
dp[i][j] = i + j;
// 在计算索引时要减一
else if (s1.charAt(i - 1) == s2.charAt(j - 1))
dp[i][j] = dp[i - 1][j - 1];
else
// 当i等于s1.length时就相当于上面的 i 等n - 1。 所以上面的dp[i + 1][j + 1]与此处的dp[i][j]是一样的。
dp[i][j] = 1 + Math.min(dp[i - 1][j], dp[i][j - 1]);
}
}
return dp[s1.length()][s2.length()];
}
对于其他字符相关的动态规划也可尝试用二维图表的方式来寻找递推关系。