1. 递归+备忘录的方式写LCS
不允许使用for和while循环
1.1 只输出最长的值
1.2 使用备忘录
memo = [[0 for j in range(len(s2)+1)] for i in range(len(s1)+1)]
def LCS(i, j, s1, s2, memo):
if i >= len(s1) or j >= len(s2):
return 0
if s1[i] == s2[j]:
if memo[i+1][j+1] == 0: #如果两个字符相同
memo[i+1][j+1] = LCS(i+1, j+1, s1, s2, memo)
memo[i][j] = 1+memo[i+1][j+1]
return memo[i][j]
else:
value1 = memo[i][j+1]
value2 = memo[i+1][j]
if memo[i][j+1] == 0: #如果两个字符不相同 弃s2[j]
value1 = LCS(i, j+1, s1, s2, memo)
if memo[i+1][j] == 0: #如果两个字符不相同 弃s1[i]
value2 = LCS(i+1, j, s1, s2, memo)
memo[i][j] = max(value1, value2)
return memo[i][j]
1.3 输出结果字符串
- 如果可以使用for和while的话,就直接遍历memo,找到数字+1的规律就可以了。
- 如果不能使用for和while的话,那就相当于再次递归,只不过这次存下的不是memo,而是根据规则,找到该结果字符串。
-
寻找结果字符串的递归思路为: memo[i][j] 是否等于 memo[i+1][j+1]+1 如果是 则为字符串的一部分 如果不是 则往左(右)走
def LCS(i, j, s1, s2, memo):
if i >= len(s1) or j >= len(s2):
return 0
if s1[i] == s2[j]:
if memo[i+1][j+1] == 0: #如果两个字符相同
memo[i+1][j+1] = LCS(i+1, j+1, s1, s2, memo)
memo[i][j] = 1+memo[i+1][j+1]
return memo[i][j]
else:
value1 = memo[i][j+1]
value2 = memo[i+1][j]
if memo[i][j+1] == 0: #如果两个字符不相同 弃s2[j]
value1 = LCS(i, j+1, s1, s2, memo)
if memo[i+1][j] == 0: #如果两个字符不相同 弃s1[i]
value2 = LCS(i+1, j, s1, s2, memo)
memo[i][j] = max(value1, value2)
return memo[i][j]
def get(i, j, s1, s2, memo, ans): #根据memo回溯 输出结果字符串
if i >= len(s1) or j >= len(s2):
return ans
if memo[i][j] == memo[i+1][j+1]+1:
ans = ans + s1[i]
ans = get(i+1, j+1, s1, s2, memo, ans)
else:
value1 = memo[i+1][j]
value2 = memo[i][j+1]
if value1 >= value2:
ans = get(i+1, j, s1, s2, memo, ans)
else:
ans = get(i, j+1, s1, s2, memo, ans)
return ans
def lcs(s1, s2):
memo = [[0 for j in range(len(s2)+1)] for i in range(len(s1)+1)]
value = LCS(0, 0, s1, s2, memo)
# for i in range(len(memo)):
# print(memo[i])
ans = ""
ans = get(0, 0, s1, s2, memo, ans)
return ans
s1 = "Look at me, I can fly!"
s2 = "Look at that, it's a fly"
print(lcs(s1, s2))
不能用for和while,到底是什么人才会想出来的题目?
2. 编辑距离 (edit distance)
编辑距离的意思 就是一个字符串 变为 另一个字符串 需要进行的 最小操作次数:
有三种操作方式:
- Insertion(插入)
- Deletion(删除)
- Substitution(替换)
举例:
d 是deletion,i是insert,s是substitution
思路:
从最后一个字符开始,字符相同,则编辑距离不变;否则:
- (i-1, j) : Deletion 向上就是删除
- (i, j-1) : Insertion 向左就是插入
- (i-1, j-1) : Substitution 左对角向上就是替换
初始化: 编辑距离有一个特别重要的前提,就是永远只对A串进行修改,因为A和B的编辑距离存在两种可能性,第一种是A变为B,第二种是B变为A。所以这里的前提是,修改A,使得A->B。
- 当i=0的时候,相当于往A里插入B字符串,即B出现一个字符,往A里插入一个;
- 当j=0的时候,相当于把A中的字符都删除,即A出现一个字符就删除一个;
(修改的永远是A,理解的时候可以看下面的table)
递归思路:
案例:
代码:
def cost(a, b, na=None, nb=None):
""" Cost of converting first na chars of a into first nb chars of b"""
if na is None: # Top level call - both na and nb will be None
na = len(a)
nb = len(b)
if na == 0 or nb == 0:
# One string is empty - n insertions or deletions reqd
return max(na, nb)
elif a[na - 1] == b[nb - 1]: # Do last chars match?
return cost(a, b, na - 1, nb - 1) # Yes - this is the align/copy case
else: # Last chars don't match
# Must delete last a, insert last b or replace last a with last b
delete_cost = 1 + cost(a, b, na - 1, nb)
insert_cost = 1 + cost(a, b, na, nb - 1)
replace_cost = 1 + cost(a, b, na - 1, nb - 1)
return min([delete_cost, insert_cost, replace_cost])
print(cost("GACTGC", "ATCTCCG"))
#ans=4
3 加权编辑距离(weighted edit distance)
编辑距离有一个前提是,所有的操作代价都相同,也就是1;
如果substitution,insertion, deletion操作所消耗的代价都不一样?
Insertion: 1
Deletion: 2
Substitution: 4
此时下图如何变化:
和编辑距离不同的地方,就在于它的初始化:删除和插入所消耗的代价都不一样。向下删除的权值变成了2,4,6,8,因为删除一次消耗的代价为2。这里有趣的一点是,因为一次substitution = 一次deletion + 一次insertion,而在这里4 > 1+2,所以substitution在这里永远不会不会被选择,还不如一次deletion,再insertion。
- 第一行:([],[ -t-i-m]) = 0 1 2 3 表示插入
- 第一列:([ -t-h-e-m], []) = 0 2 4 6 8 表示删除
左上 上 左
第二行:
- ([t], [t]) = 0,因为tt相等,直接从左上滑下来
- ([t],[ti]) = [sub=(1+4), del=(2+2), ins=(0+1)]
- ([t], [tim]) = [sub=(2+4), del=(3+2), ins=(1+1)]
第三行
- ([th], [t]) = [sub=(2+4), del=(0+2), ins=(4+1)]
- ([th], [ti]) = [sub=(0+4), del=(1+2), ins=(2+1)]
- ([th], [tim]) = [sub=(1+4), del=(2+2), ins=(3+1)]
第四行
- ([the], [t]) = [sub=(4+4), del=(2+2), ins=(6+1)]
- ([the], [ti]) = [sub=(2+4), del=(3+2), ins=(4+1)]
- ([the], [tim]) = [sub=(3+4), del=(4+2), ins=(5+1)]
第五行
- ([them], [t]) = [sub=(6+4), del=(4+2), ins=(8+1)]
- ([them], [ti]) = [sub=(4+4), del=(5+2), ins=(6+1)]
- ([them], [tim]) = 5,因为mm相同,直接从左上滑下来