问题描述
假设我们有以下两组strings:
A = "1468"
B = "4769"
如果我们想把A变成B,即想把 “1468” 变成 “4769” ,对于string A我们最少需要几步操作?
这个操作包含 删除,插入,替换。
可以发现A和B中都有数字4和6,那么我们可以保留下4,6,只针对1,8做处理
具体操作流程如下
所以我们的算法需要求:如果要把A变成B,那么在A上我们最少要进行几次操作?
即我们要计算针对string A的Edit distance。
算法思路解析
由上面可以知道,要将 string A变成 string B,可能的操作有以下四种:
删除 | 从A中删除掉多余的元素 | 对应上图中红色列 | 算 1 次操作 |
插入 | 在A中添加缺失的元素 | 对应上图中蓝色列 | 算 1 次操作 |
替换 | 将A中的元素替换为B | 对应上图中紫色列 | 算 1 次操作 |
无 | 不进行任何操作 | 对应上图中绿色列 | 算 0 次操作 |
假设 i 为string A的最后一个字符的索引,j 为string B的最后一个字符的索引。我们从两个strings的最后一个字符开始看。
case1 - 替换
如果i位置的字符要被替换为j位置的字符:
那么我们实际上只需要求A [1 ... i-1] 变成 B[1 ... j-1] 所需要的最小操作次数。然后再在此次数上再加1(因为替换i and j要算作是一次操作)。那么我们可以知道:D[ i ][ j ] = D[ i - 1 ][ j - 1 ] +1
case2 - 无操作
如果i位置的字符和j位置的字符相同:
那么我们实际上只需要求A [1 ... i-1] 变成 B[1 ... j-1] 所需要的最小操作次数(此时我们不需要加1,因为字符相同的时候不需要进行任何操作)。那么我们可以知道:D[ i ][ j ] = D[ i - 1 ][ j - 1 ]
case3 - 删除
如果i位置的字符是多余的需要被删除:
那么我们实际上只需要求A [1 ... i-1] 变成 B[1 ... j] 所需要的最小操作次数。然后再在此次数上再加1(因为删除要算作是一次操作)。那么我们可以知道:D[ i ][ j ] = D[ i - 1 ][ j ] +1
case4 - 插入
如果我们需要插入一个字符:
那么我们实际上只需要求A [1 ... i] 变成 B[1 ... j-1] 所需要的最小操作次数。然后再在此次数上再加1(因为插入要算作是一次操作)。那么我们可以知道:D[ i ][ j ] = D[ i ][ j - 1 ] +1
案例说明:
以下圆圈中需要填入坐标对应的A变成B所需要的最小操作次数。
比如,图中红色圈圈表示数组将数组 A = “” 变成数组 B = “476” 所需要的最小操作次数。
那么我们知道将空数组变成476需要新增3个数,因此最少也需要3次操作,所以圆圈内填入3。
那么针对第一行和第一列,要把空字符串变成一个字符串,或把一个字符串变成一个空字符串需要的操作次数等于字符的个数,填入数字后我们可以得到:
要求上图中红圈中的数字坐标(1,1),即求 1 变成 4 需要的次数,有以下几种方式:
情况 | 前置操作 | 当前操作 | 当前操作次数 |
情况1 | A中新增4 | 删除1 | D(0,1) + 1 = 1+1 = 2 |
情况2 | A中删除1 | 新增4 | D(1,0) + 1 = 1+1 = 2 |
情况3 | 无 | 将1替换为4 | D(0,0) + 1 = 0+1 = 1 |
由上面的表格我们可以知道最少操作次数为1,那么通过这个表格我们同样可以知道要求一个坐标对应的值我们实际上求其前置情况的最小操作次数+1,即:
因此要求下图中红色圆圈D(2,1)中的值,我们只需要看蓝色圆圈中的数字即可。此时,由于D(0,2)和D(0,1)都等于4,因此最小值就等于D(1,0)
根据此逻辑将圆圈中的数字完善,我们可以得到下面的图片。 绿色箭头表示新增操作,红色箭头表示删除操作,黄色箭头表示没有操作,蓝色箭头表示替换操作。
Implementation
# in python3
def edit_distance(A,B):
a = len(A)
b = len(B)
D = [[0 for x in range(b+1)] for x in range(a+1)] #用于获取一个坐标轴
for i in range(a+1):
for j in range(b+1):
if i == 0:
D[i][j] = j #当stringA为空时,将B中所有字符插入到A
elif j == 0:
D[i][j] = i #当stringB为空时,从A中删除掉所有字符
elif A[i-1] == B[j-1]:
D[i][j] = D[i-1][j-1]
else:
D[i][j] = 1 + min(D[i-1][j-1], D[i-1][j], D[i][j-1])
return D[a][b]
A = "key"
B = "yesterday"
print(edit_distance(A,B))