DNA序列编辑距离| 豆包MarsCode AI刷题

144 阅读3分钟

问题描述

小R正在研究DNA序列,他需要一个函数来计算将一个受损DNA序列(dna1)转换成一个未受损序列(dna2)所需的最少编辑步骤。编辑步骤包括:增加一个碱基、删除一个碱基或替换一个碱基。

题目解析

这个问题实际上是经典的字符串编辑距离问题,也称为Levenshtein距离。Levenshtein距离用于计算两个字符串之间的差异,并找到将一个字符串转换为另一个字符串所需的最少编辑操作次数。这里的编辑操作包括:

  1. 插入一个字符:在字符串的某个位置插入一个字符。
  2. 删除一个字符:从字符串中删除一个字符。
  3. 替换一个字符:将字符串中的一个字符替换为另一个字符。

动态规划解决方案

我们可以使用动态规划来解决这个问题。我们定义一个二维数组dp,其中dp[i][j]表示将dna1的前i个字符转换成dna2的前j个字符所需的最少编辑步骤次数。

状态转移方程:

如果dna1[i-1] == dna2[j-1]: 这两个字符已经匹配,不需要任何操作,所以dp[i][j] = dp[i-1][j-1]。 如果dna1[i-1] != dna2[j-1]: 我们可以选择进行插入、删除或替换操作,取这三种操作中的最小值并加1(表示当前的操作)。

表达式 dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])

dp[i-1][j]表示删除字符dna1[i-1]。 dp[i][j-1]表示在dna1的末尾插入字符dna2[j-1]。 dp[i-1][j-1]表示将dna1[i-1]替换为dna2[j-1]。

初始条件: 当i = 0时,dp[0][j]表示将空字符串转换成dna2的前j个字符,这需要进行j次的插入操作,因此dp[0][j] = j。 同样的,当j = 0时,dp[i][0]表示将dna1的前i个字符转换成空字符串,这需要进行i次删除操作,因此dp[i][0] = i。

最后返回dp[m][n],m为dna1的长度,n为dna2的长。

步骤:

  1. 初始化变量

    • m, n = len(dna1), len(dna2):获取两个DNA序列的长度。
    • dp = [[0] * (n + 1) for _ in range(m + 1)]:创建一个(m+1) x (n+1)的二维数组dp,用于存储子问题的解。
  2. 初始化dp数组

    • i = 0时,将空字符串转换为dna2的前j个字符需要j次插入操作,因此dp[0][j] = j
    • j = 0时,将dna1的前i个字符转换为空字符串需要i次删除操作,因此dp[i][0] = i
  3. 填充dp数组

    • 对于dp[i][j],如果dna1[i-1] == dna2[j-1],则不需要任何操作,dp[i][j] = dp[i-1][j-1]
    • 否则,选择插入、删除或替换操作中的最小值,并加1,即dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
  4. 返回结果

    • 最后返回dp[m][n],即将整个dna1转换为整个dna2所需的最少编辑步骤数。

代码如下

def solution(dna1, dna2):
# Please write your code here
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]

image.png