这里简单讲解一下 [DNA序列编辑距离](DNA序列编辑距离 - MarsCode) 这道题目,我在写的时候还是遇到了一点小坑的。
题目大意:
小R正在研究DNA序列,他需要一个函数来计算将一个受损DNA序列(dna1)转换成一个未受损序列(dna2)所需的最少编辑步骤。编辑步骤包括:增加一个碱基、删除一个碱基或替换一个碱基。
测试样例(这里直接给代码中的样例了,不太清楚题目描述里的样例有啥用):
样例1:
输入:dna1 = "AGCTTAGC",dna2 = "AGCTAGCT"
输出:2
样例解释:
删除dna1中的第4或5个字符T,删除dna2中的最后一个字符T
样例2:
输入:dna1 = "AGCCGAGC",dna2 = "GCTAGCT"
输出:4
样例解释:
删除dna1中的第1和第4个字符,删除dna2中的最后一个字符,将dna1中的第5个字符G替换为T
求解思路
先讲一下我遇到的坑。
第一眼看到题感觉是最长公共子序列的变形,求解dna1和dna2的最长公共子序列,然后用dna1和dna2的长度和减去2倍最长公共子序列长度即为答案。但是大意了,没有考虑替换这一情况,从上面的第二个样例就能看出。
于是只能转换思路,直接求解所需要的最小替换次数。
依然没有跳出最长公共子序列的框架(不了解最长公共子序列的可以先去LeetCode找题先做一下),只是从逆向求解变成正向求解。
dp数组含义:dp[i][j]为使dna1中前i个与dna2中前j个元素组成的字串相等的最小操作数
-
初始化边界条件:
dp[i][0] = i和dp[0][j] = j表示将一个空字符串转换成另一个字符串需要i或j次操作。
-
状态转移:
- 如果
dna1[i-1] == dna2[j-1],表示不需要额外操作。dp[i][j] = dp[i-1][j-1] - 表示选择删除、插入或替换操作中的最小值,并加上一次操作,
dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
- 如果
AC代码如下:
def solution(dna1, dna2):
print(dna1, dna2)
# 获取dna1和dna2的长度
len1, len2 = len(dna1), len(dna2)
# 初始化动态规划数组
dp = [[0] * (len2 + 1) for _ in range(len1 + 1)]
# 初始化边界条件
for i in range(1, len1 + 1):
dp[i][0] = i
for j in range(1, len2 + 1):
dp[0][j] = j
# 遍历dp数组求解
for i in range(1, len1 + 1):
for j in range(1, len2 + 1):
if dna1[i-1] == dna2[j-1]:
dp[i][j] = dp[i-1][j-1]
else:
dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
# 返回编辑距离
return dp[len1][len2]