动态规划----编辑距离

1 题目

72. 编辑距离

给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数  。

你可以对一个单词进行如下三种操作:

插入一个字符
删除一个字符
替换一个字符

示例 1:

输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')
示例 2:

输入:word1 = "intention", word2 = "execution"
输出:5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')

提示:

0 <= word1.length, word2.length <= 500
word1 和 word2 由小写英文字母组成

2 分析

2.1 暴力递归

最值问题---->dp
涉及到两个字符串---->二维dp, dp[i][j]
step1 定义dp[i][j] 
  dp[i][j] 表示将单词word1[0:i]转换成word2[0:j]所使用的最小操作数
step2 base case
  dp[0][j] 当word1已经遍历完时,将wrod2的剩余字符添加到word1中即可。求取操作数,直接返回剩余word2长度即可。
  dp[i][0] 当word2已经遍历完时,将word1的剩余字符删除即可。求取操作数,直接返回剩余word1长度即可。
step3 递推关系(最优子结构)
  dp(i, j) = dp(i + 1, j + 1)          跳过 (word1[i] == word2[j]) 不采取任何操作,计数不增加
           = min(
                 dp(i + 1, j)  + 1         删除 (word1[i] != word2[j], j 不变,将word1[i] 删除,即i指针向前移动一位) 
                 dp(i , j + 1) + 1         插入 (word1[i] != word2[j], i 不变,将字符插入word1中。即word2[j]匹配插入字符,j指针向前移动一位) 
                 dp(i + 1, j + 1) + 1      替换 (word1[i] != word2[j], 将word1[i] 替换为word2[j], 当前位置匹配,同时向前移动两个指针)
                )

代码:

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
       
        def dp(i,j):
            # 从底到顶,暴力递归。
            # base case
            if i == len(word1) and j == len(word2):
                return 0
            if i == len(word1):
                return len(word2) - j
            if j == len(word2):
                return len(word1) - i
            
            # 递推关系
            if word1[i] == word2[j]:
                return dp(i + 1, j + 1)
            else:
                return min(dp(i + 1, j), dp(i, j + 1), dp(i + 1, j + 1)) + 1

        return dp(0, 0)

2.2 暴力递归 + 优化重复子问题

分析:从递推关系式 或者 画个树状图 可以看出存在重复子问题,通过备忘录 或 dp table 优化重复子问题

备忘录

将所有返回值更新在备忘录 memo 中,dp 函数最后返回 memo[i][j] 即可

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        # 因为初始化所有值为-1,因为查询 i == len(word1) 时查询了 memo[i][j],所以长度为memo[m+1][n+1]
        memo = [[-1 for j in range(len(word2) + 1)] for i in range(len(word1) + 1)]
        def dp(i,j):
            if memo[i][j] != -1:
                return memo[i][j]

            if i == len(word1) and j == len(word2):
                memo[i][j] = 0
                return memo[i][j] 
            if i == len(word1):
                memo[i][j] = len(word2) - j
                return memo[i][j]
            if j == len(word2):
                memo[i][j] = len(word1) - i
                return memo[i][j]

            if word1[i] == word2[j]:
                memo[i][j] = dp(i + 1, j + 1)
            else:
                memo[i][j] = min(dp(i + 1, j), dp(i, j + 1), dp(i + 1, j + 1)) + 1
            
            return memo[i][j]
        return dp(0, 0)

dp table

已知边界条件,直接先初始化边界条件。并从边界条件开始递推目标值。

posted @ 2023-06-01 16:27  柳叶昶  阅读(5)  评论(0编辑  收藏  举报