# -*- coding: utf-8 -*-
'''
Python程序员面试算法宝典---解题总结: 第5章 字符串 5.19 如何求字符串的编辑距离
题目:
编辑距离又称为Levenshtein距离,是指两个字符串之间由一个转成另一个所需的
最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符、
插入一个字符、删除一个字符。请设计并实现一个算法来计算两个字符串
的编辑距离,并计算其复杂度。
在某些应用场景下,替换操作的代价比较高,假设替换操作的代价
是插入和删除的两倍,算法该如何调整。
分析:
这是之前见过的一道题目。
举例:
假设字符串为abce
另一个字符串为bcfd
那么整个过程如下:
可以删除字符a,然后替换字符e为f,最后添加字符d。
计算两个字符串的编辑距离就是看如何通过每次3种操作得到。
这个应该可以用递归实现,因为是求最优解。
可以通过回溯求出所有的情况,然后凡是某种编辑距离已经大于之前
记录的最小编辑距离就通过剪枝不再处理。
递归基:
当编辑后的字符串str1已经等于str2,说明找到了一种编辑方法
递归步:
如何选择当前需要处理的str1中的字符。
关键:
1 书上解法
动态规划,d[i][j]表示字符串str1长度为i的子串与第二个
字符串str2中长度为j的子串的编辑距离。
d[i][j]可以来自于3种情况:
添加操作:
d[i][j-1]通过s1长度为i的字符串添加字符s2[j]
删除操作:
d[i-1][j]通过s1长度为i的字符串删除字符s1[j]
替换操作:
加入s1[i] == s2[j],则
d[i][j] = d[i-1][j-1]
否则
d[i][j] = d[i-1][j-1] + 1(如果替换的权重为2,就修改这个1为2)
边界情况:
d[0][j]=j
d[i][0]=i
因为字符串与空串的编辑距离为字符串长度
所求目标:
d[len(str1)][len(str2)]
2 动态规划
动态规划本身是基于递推实现的。
有公式组成,通过边界递推到最终。
阶段: 问题分成有顺序的几个环节
状态: 状态是对问题在某一时刻的进展情况的数学描述
决策: 从某阶段的一个状态到下一个状态所作的选择
状态转移方程: 根据上一阶段的状态和决策推导出本阶段的状态。
本质:
1 递推 + 方程
2 最优决策是在最后阶段形成,然后向前倒退的,直到初始阶段;
但是决策的具体结果以及状态迁移却是通过初始阶段
开始计算然后向后递推得到的
性质:
最优化原理,无后向性
最优化原理: 最优子结构
无后向性: 某状态以后过程不会影响以前的状态,只与当前状态有关。
设计动态规划算法步骤:
1) 划分阶段
2) 选择状态
3) 确定决策写出状态迁移方程
3 之所以没有想到
还是对动态规划理解得不深刻,
状态迁移方程中,如果对两个元素(例如两个字符串处理)
那就可以用d[i][j]表示前缀的状态
参考:
Python程序员面试算法宝典
算法设计与分析
'''
def getEditDistance(str1, str2, replaceWeight=1):
if not str1 and not str2:
return 0
elif not str1:
len1 = len(str1)
return len1
elif not str2:
len2 = len(str2)
return len2
len1 = len(str1)
len2 = len(str2)
d = [[0] * (len2 + 1) for i in range(len1 + 1)]
# 初始化边界情况d[i][0] = i
for i in range(len1 + 1):
d[i][0] = i
for j in range(len2 + 1):
d[0][j] = j
# 开始递推
for i in range(1, len1 + 1):
for j in range(1, len2 + 1):
appendValue = d[i - 1][j] + 1
deleteValue = d[i][j - 1] + 1
replaceValue = d[i - 1][j - 1]
# 注意,这里比较的是str1[i-1]与str2[j-1]
if str1[i - 1] != str2[j - 1]:
replaceValue += replaceWeight
d[i][j] = min(appendValue, deleteValue, replaceValue)
else:
d[i][j] = min(appendValue, deleteValue, replaceValue)
# 最终结果计算的是d[len1][len2]
result = d[len1][len2]
return result, d
def process():
str1 = "bciln"
str2 = "fciling"
replaceWeight = 1
result, d = getEditDistance(str1, str2, replaceWeight)
for arr in d:
print arr
print result
replaceWeight = 2
result, d = getEditDistance(str1, str2, replaceWeight)
for arr in d:
print arr
print result
if __name__ == "__main__":
process()