最小编辑距离
编辑距离有两种:
- Levenshtein距离: 允许插入,删除和替换一个字符, 最常见
- Damerau-Levenshtein距离: 在上者的基础上, 允许交换相邻两字符的位置, 在git中使用
另外, 操作的权重不同可以分为: 非加权, 和 加权
应用场景:
- DNA 序列的比对 (两段DNA, 如果编辑距离很小, 说明可能是同源DNA发生了一点突变)
- 拼写检查, 最接地气的了
LeetCode上第72题(72. 编辑距离 - 力扣(LeetCode) (leetcode-cn.com))
另外CS61a中在作业cat
里也要求实现最小编辑距离
一般两种做法:
- 递归( 自顶向下, 会有很多的重复计算, 不过可以采用memo, 但是效果应该还不是很好)
- 动规( 自底向上, 采用memo)
string1和string2 都可以插入, 删除, 和替换一个字符, 可以等价为对:
- string1 插入一个字符
- string2 插入一个字符 (删除string1的一个字符)
- 修改string1中的一个字符
且在基础的levenshtein编辑距离中, 三种操作的代价是相同的, 实际上, 他们的代价可以是不同的, 这就是加权编辑距离(可能更符合实际场景), 算法实现, 就是在状态转变的时候加上对应的代价.
动规的一点解释: 二维数组res, res(i)(j), 代表 string1 的前 i 个字符要转化 到 string2 的前 j 个字符需要的编辑距离
另外: 并没有作业要求记录编辑路径, 可以作为拓展作业.
def minimum_mewtations(start, goal, limit):
"""A diff function that computes the edit distance from START to GOAL.
This function takes in a string START, a string GOAL, and a number LIMIT.
Arguments:
start: a starting word
goal: a goal word
limit: a number representing an upper bound on the number of edits
>>> big_limit = 10
>>> minimum_mewtations("cats", "scat", big_limit) # cats -> scats -> scat
2
>>> minimum_mewtations("purng", "purring", big_limit) # purng -> purrng -> purring
2
>>> minimum_mewtations("ckiteus", "kittens", big_limit) # ckiteus -> kiteus -> kitteus -> kittens
3
"""
# assert False, 'Remove this line'
# def1 iter 缺点: 计算次数多,重复计算, 且没办法提前终止
def iter_func(s, g, limit):
if len(s) == 0 or len(g) == 0:
return max(len(s), len(g))
elif s[-1] == g[-1]:
return iter_func(s[:-1], g[:-1], limit)
else:
return 1 + min(iter_func(s, g[:-1], limit),iter_func(s[:-1], g,limit),iter_func(s[:-1], g[:-1], limit))
# return iter_func(start, goal, limit)
# def2 iter 缺点: 计算次数多,重复计算, 可以采用备忘录的方式来记录计算
def iter_func(s, g, lim):
if len(s) == 0 or len(g) == 0:
return max(len(s), len(g))
elif s[-1] == g[-1]:
return iter_func(s[:-1], g[:-1], lim)
elif lim>= limit:
return limit + 1;
else:
return 1 + min(iter_func(s, g[:-1], lim + 1), iter_func(s[:-1], g, lim + 1), iter_func(s[:-1], g[:-1], lim + 1))
# return iter_func(start, goal, 0)
# def3 iter 自底向上, 外加memo (自顶向下也可以)
def down_up(s, g, limit):
ls = len(s)
lg = len(g)
res_array = [[0 for _ in range(lg + 1)] for _ in range(ls + 1)]
for x in range(1, lg +1):
res_array[0][x] = x
for x in range(1, ls + 1):
res_array[x][0] = x
for i in range(1, ls + 1):
for j in range(1, lg + 1):
if s[i-1] == g[j-1]:
res_array[i][j] = res_array[i-1][j-1]
else:
res_array[i][j] = min(res_array[i-1][j-1] , res_array[i][j-1], res_array[i-1][j]) + 1
# print(res_array)
return res_array[-1][-1]
return down_up(start, goal, limit)
#todo 记录编辑路径
参考:
- https://www.cnblogs.com/arkenstone/p/6196111.html