问题描述
小R正在研究DNA序列,他需要一个函数来计算将一个受损DNA序列(dna1)转换成一个未受损序列(dna2)所需的最少编辑步骤。编辑步骤包括:增加一个碱基、删除一个碱基或替换一个碱基
样例1:
输入:
dna1 = "AGT",dna2 = "AGCT"
输出:1
样例2:
输入:
dna1 = "AACCGGTT",dna2 = "AACCTTGG"
输出:4
样例3:
输入:
dna1 = "ACGT",dna2 = "TGC"
输出:3
样例4:
输入:
dna1 = "A",dna2 = "T"
输出:1
样例5:
输入:
dna1 = "GGGG",dna2 = "TTTT"
输出:4
算法思路
在计算机科学和生物信息学领域,DNA序列比对是一个重要的研究课题。比对DNA序列的过程通常需要判断两条序列之间的相似程度,而计算最小编辑距离(Minimum Edit Distance)是解决该问题的一种经典方法。本文将详细阐述如何利用动态规划解决两个DNA序列的最小编辑距离问题,并附带个人的思考和分析。
-
定义一个二维数组 dp[i][j] 表示将 dna1[0...i-1] 转换为 dna2[0...j-1] 所需的最少编辑步骤。
-
递推公式:
• 如果 dna1[i-1] == dna2[j-1],说明这两个字符相同,不需要额外操作:
dp[i][j] = dp[i-1][j-1]
• 否则,取三种操作中的最小值(加、删、替换):
dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])
• dp[i-1][j] : 删除一个字符
• dp[i][j-1] : 插入一个字符
• dp[i-1][j-1] : 替换一个字符
- 边界条件:
• dp[i][0] = i : dna1 长度为 i 时,转换为空字符串需要删除 i 次。
• dp[0][j] = j : 空字符串转换为 dna2 长度为 j 时,需要插入 j 次。
- 最终结果为 dp[m][n] ,其中 m 和 n 是 dna1 和 dna2 的长度。
代码实现
def min_edit_distance(dna1: str, dna2: str) -> int:
m, n = len(dna1), len(dna2)
# 创建DP表,初始化为全0
dp = [[0] * (n + 1) for _ in range(m + 1)]
# 初始化边界条件
for i in range(m + 1):
dp[i][0] = i
for j in range(n + 1):
dp[0][j] = j
# 填充DP表
for i in range(1, m + 1):
for j in range(1, n + 1):
if dna1[i - 1] == dna2[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
else:
dp[i][j] = 1 + min(dp[i - 1][j], # 删除
dp[i][j - 1], # 插入
dp[i - 1][j - 1]) # 替换
return dp[m][n]
# 测试样例
dna1 = "AGT"
dna2 = "AGCT"
print(min_edit_distance(dna1, dna2)) # 输出:1
算法分析与思考*
1. 时间复杂度和空间复杂度:
• 时间复杂度为 O(m * n) ,因为需要填充一个大小为 m * n 的动态规划表。
• 空间复杂度为 O(m * n) ,存储整个表所需的空间。然而,优化后可以将空间复杂度降为 O(min(m, n)) ,通过滚动数组存储状态。
2. 实际应用:
该算法在生物信息学中非常实用,尤其是基因序列的相似性分析。除此之外,它还广泛应用于文本编辑器(如拼写检查)和版本控制工具中。
3. 拓展思考:
在实际DNA序列分析中,可能需要额外考虑碱基的权重差异。例如,某些碱基对的替换可能代价更高(如从 A 替换为 G),这些复杂性可通过修改状态转移公式进一步处理。