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

118 阅读4分钟

题目内容

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

题目分析

问题理解

这道题实际上是一道线性DP的题目,可以将其描述为:

给定两个字符串A和B,现在要将A经过若干操作变为B,可进行的操作有:

  • 删除——将字符串A中的某个字符删除
  • 插入——在字符串A的某个位置插入某个字符
  • 替换——将字符串A中的某个字符替换为另一个字符

现在请你求出,将A变为B至少需要进行多少次操作。

定义状态

我们使用一个二维数组 dp,其中 dp[i][j] 表示将 dna1 的前 i 个字符转换成 dna2 的前 j 个字符所需的最少编辑步骤。

初始化

首先,我们需要初始化 dp 数组。dp[i][0] 表示将 dna1 的前 i 个字符转换成空字符串所需的编辑步骤数,显然需要 i 次删除操作。同理,dp[0][j] 表示将空字符串转换成 dna2 的前 j 个字符所需的编辑步骤数,显然需要 j 次插入操作。

状态转移

接下来,我们填充 dp 数组。对于每个 dp[i][j],我们有以下三种情况:

  1. 如果 dna1[i-1] == dna2[j-1]

    • 这意味着 dna1 的第 i-1 个字符和 dna2 的第 j-1 个字符相同,不需要进行任何编辑操作。
    • 因此,dp[i][j] = dp[i-1][j-1]
  2. 如果 dna1[i-1] != dna2[j-1]

    • 这意味着我们需要进行编辑操作。我们有三种选择:

      • 插入操作:在 dna1 的第 i 个位置插入 dna2[j-1],这样 dna1 的前 i 个字符就变成了 dna2 的前 j-1 个字符。因此,dp[i][j] = dp[i][j-1] + 1
      • 删除操作:删除 dna1 的第 i-1 个字符,这样 dna1 的前 i-1 个字符就变成了 dna2 的前 j 个字符。因此,dp[i][j] = dp[i-1][j] + 1
      • 替换操作:将 dna1 的第 i-1 个字符替换为 dna2 的第 j-1 个字符,这样 dna1 的前 i-1 个字符就变成了 dna2 的前 j-1 个字符。因此,dp[i][j] = dp[i-1][j-1] + 1
    • 我们选择这三种操作中的最小值:dp[i][j] = min(dp[i][j-1] + 1, dp[i-1][j] + 1, dp[i-1][j-1] + 1)

最终结果

dp[len(dna1)][len(dna2)] 即为将 dna1 转换成 dna2 所需的最少编辑步骤。

具体例子

dna1 = "AGT", dna2 = "AGCT"

  1. 初始化

    • dp[0][j]:将空字符串转换成 dna2 的前 j 个字符。

      • dp[0][0] = 0
      • dp[0][1] = 1(插入 'A')
      • dp[0][2] = 2(插入 'A' 和 'G')
      • dp[0][3] = 3(插入 'A'、'G' 和 'C')
      • dp[0][4] = 4(插入 'A'、'G'、'C' 和 'T')
    • dp[i][0]:将 dna1 的前 i 个字符转换成空字符串。

      • dp[1][0] = 1(删除 'A')
      • dp[2][0] = 2(删除 'A' 和 'G')
      • dp[3][0] = 3(删除 'A'、'G' 和 'T')
  2. 状态转移

AGCT
01234
A10123
G21012
T32111
  1. 最终结果

dp[3][4] = 1,即将 dna1 = "AGT" 转换成 dna2 = "AGCT" 所需的最少编辑步骤为 1。

参考代码

def solution(dna1, dna2):
    m, n = len(dna1), len(dna2)
    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
    
    # 动态规划
    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 - 1] + 1,  # 替换
                               dp[i - 1][j] + 1,      # 删除
                               dp[i][j - 1] + 1)      # 插入
    
    return dp[m][n]

总结

  • 时间复杂度O(m * n),其中 m 和 n 分别是 dna1 和 dna2 的长度。
  • 空间复杂度O(m * n),用于存储 dp 数组。

通过动态规划,我们可以有效地计算出将一个DNA序列转换成另一个DNA序列所需的最少编辑步骤。这个方法不仅适用于DNA序列,还可以应用于其他字符串编辑距离问题。