动态规划:编辑距离

题目描述

UNIX系统下有一个行编辑器ed,它每次只对一行文本做删除一个字符、插入一个字符或替换一个字符三种操作。例如某一行的内容是“ABC”,经过把第二个字符替换成“D”、删除第一个字符、末尾插入一个字符“B”,这三步操作后,内容就变成了“DCB”。即“ABC”变成“DCB”需要经过3步操作,我们称它们的编辑距离为3。
现在给你两个任意字符串(不包含空格),请帮忙计算它们的最短编辑距离。

输入描述:
输入包含多组数据。

每组数据包含两个字符串m和n,它们仅包含字母,并且长度不超过1024。

输出描述:
题目描述

分析:

假设序列S和T的长度分别为m和n, 两者的编辑距离表示为 d p [ m ] [ n ] dp[m][n] dp[m][n]. 则对序列进行操作时存在以下几种情况:

  • a, 当S和T的末尾字符相等时, 对末尾字符不需要进行上述定义操作中(亦即"编辑")的任何一个, 也就是不需要增加计数. 则满足条件: d p [ m ] [ n ] = d p [ m − 1 ] [ n − 1 ] dp[m][n] = dp[m - 1][n - 1] dp[m][n]=dp[m1][n1].
  • b, 当S和T的末尾字符不相等时, 则需要对两者之一的末尾进行编辑, 相应的计数会增加1.
    • b1, 对S或T的末尾进行修改, 以使之与T或S相等, 则此时 d p [ m ] [ n ] = d p [ m − 1 ] [ n − 1 ] + 1 dp[m][n] = dp[m - 1][n - 1] + 1 dp[m][n]=dp[m1][n1]+1;
    • b2, 删除S末尾的元素, 使S与T相等, 则此时 d p [ m ] [ n ] = d p [ m − 1 ] [ n ] + 1 dp[m][n] = dp[m - 1][n] + 1 dp[m][n]=dp[m1][n]+1;
    • b3, 删除T末尾的元素, 使T与S相等, 则此时 d p [ m ] [ n ] = d p [ m ] [ n − 1 ] + 1 dp[m][n] = dp[m][n - 1] + 1 dp[m][n]=dp[m][n1]+1;
    • b4, 在S的末尾添加T的尾元素, 使S和T相等, 则此时S的长度变为m+1, 但是此时S和T的末尾元素已经相等, 只需要比较S的前m个元素与T的前n-1个元素, 所以满足 d p [ m ] [ n ] = d p [ m ] [ n − 1 ] + 1 dp[m][n] = dp[m][n - 1] + 1 dp[m][n]=dp[m][n1]+1;
    • b5, 在T的末尾添加S的尾元素, 使T和S相等, 此时的情况跟b4相同, 满足 d p [ m ] [ n ] = d p [ m − 1 ] [ n ] + 1 dp[m][n] = dp[m - 1][n] + 1 dp[m][n]=dp[m1][n]+1;
  • c, 比较特殊的情况是, 当S为空时, d p [ 0 ] [ n ] = n dp[0][n] = n dp[0][n]=n; 而当T为空时, d p [ m ] [ 0 ] = m dp[m][0] = m dp[m][0]=m; 这个很好理解, 例如对于序列"“和"abc”, 则两者的最少操作为3, 即序列""进行3次插入操作, 或者序列"abc"进行3次删除操作.

所以, 编辑距离的动态规划方程为:
编辑距离图片
或者:
公式

算法计算步骤:

1.对于字符串A ‘jarrry’和字符串B’jerr’,先初始化矩阵dp为 [ l e n ( A ) + 1 ] [ l e n ( B ) + 1 ] [len(A) + 1][len(B) + 1] [len(A)+1][len(B)+1],dp 矩阵的第一行与第一列均从零开始递增,最后得矩阵为

初始化dp矩阵

2.然后从第一列开始循环。对于每个矩阵坐标 (i,j),设置中间变量temp

  • 当 A[i] == B[j] 时,temp = 1;否则 temp = 0。

d p [ i ] [ j ] = m i n ( d p [ i − 1 ] [ j − 1 ] + t e m p , m i n ( d p [ i − 1 ] [ j ] + 1 , d p [ i ] [ j − 1 ] + 1 ) ) dp[i][j] = min(dp[i-1][j-1] + temp ,min(dp[i-1][j] + 1 , dp[i][j-1] + 1)) dp[i][j]=mindp[i1][j1]+tempmin(dp[i1][j]+1,dp[i][j1]+1

3.循环完成dp矩阵为
dp结果矩阵

d p [ l e n ( A ) ] [ l e n ( B ) ] dp[len(A)][len(B)] dp[len(A)][len(B)] 就是A,B两个字符串得编辑距离

注意:

  • 分配保存结果的空间时,需要指定等于字符串长度+1的空间。
  • 所以,在更新数组的时候,比较的是 i-1 和 j-1的值,从而确定布尔变量。

O(m*n) space

def edit_distance(word1, word2):
    len1 = len(word1)
    len2 = len(word2)
    # 分配存储空间
    # 新建数组保存结果的距离
    dp = np.zeros((len1 + 1,len2 + 1))
    # 初始化第一行和第一列
    for i in range(len1 + 1):
        dp[i][0] = i;     
    for j in range(len2 + 1):
        dp[0][j] = j;
    # 遍历矩阵中的每一个元素,按照动态规划方程来更新
    # 递归、迭代,求解矩阵中剩余的其他元素
    for i in range(1, len1 + 1):
        for j in range(1, len2 + 1):
            delta = 0 if word1[i-1] == word2[j-1] else 1
            dp[i][j] = min(dp[i - 1][j - 1] + delta, min(dp[i-1][j] + 1, dp[i][j - 1] + 1))
    return dp[len1][len2]

O ( n ) O(n) O(n) space

  • 因为每次更新距离的时候,只需要用到上一行(即上一字符)的距离信息,所以只需要构建一个长度为 n 的数组,保存前一字符的距离信息。
import numpy as np
class Solution(object):
    def minDistance(self, word1, word2):
        """
        :type word1: str
        :type word2: str
        :rtype: int
        """
        # 典型的动态规划题目
        # 通过总结原理,寻找规律之后,发现表达式
        # 新建数组保存结果 
        # O(n) space
        l1 = len(word1)+1
        l2 = len(word2)+1
        # 定义一个数组,保存前一行的距离值
        pre = [0 for _ in xrange(l2)]
        # 初始化第一行
        for i in range(l2):
            pre[i] = i
            
        # 递归、迭代,求解矩阵中剩余的其他元素
        for i in range(1, l1):
            # 初始化为当前行索引
            cur = [i]*l2
            for j in range(1, l2):
                cur[j] = min(pre[j]+1,cur[j-1]+1,pre[j-1]+(word1[i-1]!=word2[j-1]))
            pre = cur[:]
        return int(pre[-1])
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值