五一几天出去玩了,没有时间来刷题,今天起又要坚持了。昨天帮朋友笔试,抽到了最小编辑距离算法,这是一个经典的DP算法,网上的讲解很多也很杂,我就用的理解写一下。
问题描述:
A是一篇杂志的文字校验员,负责改正文章里面的错字错句,我们要实现一个程序来统计A一天的工作量。A的操作分为三类:更改一个字、删除一个字或者增加一个字,我们需要通过对比校验前后的文章统计A最小需要操作多少次。为简化,我们假设文章的每行只包含数字和字母,不含空格等特殊字符。
输入描述:
每一行输入为正整数N,表示文章的总行数(0<N<=10000)
后面N行,为校验前的文章
再后面N行,为校验后的文章
示例1:
输入
2
abcdef
123456
bcdg
234567
输出
5
说明
删除了a,用g替换了e,删除了f,删除了1,增加了7,共操作5次
解题思路:
- 题目主要是进行多次最小编辑距离的计算,只要我们编写一个计算最小编辑距离的函数即可,多次调用,每次返回最短距离的结果,相加即可输出。问题是怎么计算两个字符串的编辑距离了,例如现在有两个字符串 s = a b c d e f s=abcdef s=abcdef和 t = b c d g t=bcdg t=bcdg,我们怎么知道他们的最小编辑距离是多少了?
- 所以就要拿出我们的利器动态规划了,我们不用关心他的过程是怎么样的,我们只用关注最短距离结果。动态规划方法是把问题向前分解,想要解决一个问题,需要先解决这个问题的子问题,那么要解决子问题,又需要解决子问题的子问题。通过先解决最小的子问题,再不断的解决更上一层的问题,那么就可以解决最终的问题。
- 动态规划最经常的套路是定义转状态和找到状态转移方程。
首先对问题进行定义:
dp[i[j] 代表着 s 中前 i 个字符转移到 t 中前 j 个字符的最小编辑距离
状态转移公式:
先做一个二维数组找找规律,我们先填好数组的边界条件,当 s = 0 s=0 s=0时此时源字符串为空,到达目标字符串 t t t的最小编辑距离为字符串本身的长度,进行的操作为不断添加一个字符。所以dp[0][j]的最小编辑距离为j;当 t = 0 t=0 t=0时此时目标字符串为空,源字符串转移到目标字符串的方式为删除,有多少个删除多少个,所以dp[i][0]的最小编辑距离为i。
b | c | d | g | ||
---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | |
a | 1 | ||||
b | 2 | ||||
c | 3 | ||||
d | 4 | ||||
e | 4 | ||||
f | 4 |
- 接下来我们尝试从dp[1][1]出发,此时的dp[1][1]的来源方向有三个,分别是dp[0[0],dp[0][1],dp[1][0],他们代表着不同的含义。此时题目中描述了有三种操作:
- 删除一个字符
- s中删除一个字符变成t,也就是dp[1][1]=d[0][1]+1
- 增加一个字符
- s中增加一个字符变成t,也就意味着t删除一个字符,也就是dp[1][1]=d[1][0]+1
- 替换一个字符
- s中删除一个字符变成t,也就是dp[1][1]=d[0][0]+1
- 删除一个字符
- 我们应该去想想这个问题,既然有三种操作的话,那应该也有不用操作的情况。当 s [ i ] = = t [ j ] s[i]==t[j] s[i]==t[j]的时候,意味着s中第i个字符和t中第j个字符相等,此时dp[i][j]=dp[i-1][j-1],例如 i = 2 , j = 1 i=2,j=1 i=2,j=1时 s = a b , t = b s=ab,t=b s=ab,t=b时s和t的最后一个相等,dp[2][1]=dp[1][0]
此时表格也就变成了这样
b | c | d | g | ||
---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | |
a | 1 | 1 \color{blue}{1} 1 | 2 | 3 | 4 |
b | 2 | 1 | 2 | 3 | 4 |
c | 3 | 2 | 1 | 2 | 3 |
d | 4 | 3 | 2 | 1 | 2 |
e | 5 | 4 | 3 | 2 | 2 |
f | 6 | 5 | 4 | 3 | 3 \color{red}{3} 3 |
代码如下:
def mearch(s,t):
dp = [[0]*(len(t)+1) for _ in range(len(s)+1)]
for i in range(len(s)+1):
dp[i][0] = i
for i in range(len(t)+1):
dp[0][i] = i
for i in range(1,len(s)+1):
for j in range(1,len(t)+1):
if s[i-1] == t[j-1]:
dp[i][j] = dp[i-1][j-1]
else:
dp[i][j] = min(dp[i-1][j-1], dp[i][j-1],dp[i-1][j])+1
return dp[len(s)][len(t)]
n = int(input())
res = 0
origin = list()
for i in range(n):
origin.append(input())
after = list()
for i in range(n):
after.append(input())
for i in range(n):
res += mearch(origin[i], after[i])
print(res)