DNA序列编辑距离
问题描述
小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序列(dna1
)转换成一个未受损序列(dna2
)所需的最少编辑步骤。编辑步骤包括:
- 增加一个碱基
- 删除一个碱基
- 替换一个碱基
数据结构选择
我们选择使用动态规划(Dynamic Programming, DP)来解决这个问题。动态规划是一种通过将问题分解为子问题并存储子问题的解来解决复杂问题的方法。
算法步骤
-
定义状态:
- 我们使用一个二维数组
dp
,其中dp[i][j]
表示将dna1
的前i
个字符转换成dna2
的前j
个字符所需的最少编辑步骤。
- 我们使用一个二维数组
-
初始化:
- 当
dna2
为空时,将dna1
转换为空序列需要删除dna1
的所有字符,因此dp[i][0] = i
。 - 当
dna1
为空时,将空序列转换为dna2
需要增加dna2
的所有字符,因此dp[0][j] = j
。
- 当
-
状态转移:
-
如果
dna1[i-1] == dna2[j-1]
,则不需要进行任何编辑操作,dp[i][j] = dp[i-1][j-1]
。 -
否则,我们有三种选择:
- 删除
dna1[i-1]
,dp[i][j] = dp[i-1][j] + 1
- 增加
dna2[j-1]
,dp[i][j] = dp[i][j-1] + 1
- 替换
dna1[i-1]
为dna2[j-1]
,dp[i][j] = dp[i-1][j-1] + 1
- 删除
-
取这三种操作的最小值作为
dp[i][j]
。
-
-
最终结果:
dp[m][n]
即为将dna1
转换为dna2
所需的最少编辑步骤,其中m
和n
分别是dna1
和dna2
的长度。
代码实现:
m, n = len(dna1), len(dna2)
# 创建一个 (m+1) x (n+1) 的二维数组 dp
dp = [[0] * (n + 1) for _ in range(m + 1)]
# 初始化 dp 数组
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] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
# 返回最终结果
return dp[m][n]
if __name__ == "__main__":
# 你可以添加更多测试用例
print(solution("AGCTTAGC", "AGCTAGCT") == 2)
print(solution("AGCCGAGC", "GCTAGCT") == 4)
结
- 通过动态规划,我们可以有效地计算出将一个DNA序列转换为另一个DNA序列所需的最少编辑步骤。
- 关键在于理解状态转移方程和初始化条件。