青训营 X 豆包 MarsCode 技术训练营第六课 DNA序列编辑距离 | 豆包 MarsCode AI 刷题

39 阅读3分钟

问题分析

在生物信息学中,DNA序列编辑距离(也称为Levenshtein距离)是一个重要的概念,用于衡量两个序列之间的差异。具体来说,它表示将一个序列转换成另一个序列所需的最少编辑步骤,其中每一步可以是插入一个碱基、删除一个碱基或替换一个碱基。

解题思路

为了计算两个DNA序列之间的编辑距离,我们可以使用动态规划(Dynamic Programming)的方法。动态规划是一种将复杂问题分解成更小的子问题,并存储子问题的解以避免重复计算的技术。

动态规划表

我们可以使用一个二维数组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],则dp[i][j] = min(dp[i-1][j] + 1, dp[i][j-1] + 1, dp[i-1][j-1] + 1),分别对应删除、插入和替换操作。

初始化

  • dp[0][j] = j,表示将空序列转换成dna2的前j个字符需要j次插入操作。
  • dp[i][0] = i,表示将dna1的前i个字符转换成空序列需要i次删除操作。

具体实现

以下是使用动态规划计算DNA序列编辑距离的Python代码示例:

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

# 测试样例
print(edit_distance("AGT", "AGCT"))  # 输出: 1
print(edit_distance("AACCGGTT", "AACCTTGG"))  # 输出: 4
print(edit_distance("ACGT", "TGC"))  # 输出: 3
print(edit_distance("A", "T"))  # 输出: 1
print(edit_distance("GGGG", "TTTT"))  # 输出: 4

测试样例验证

  1. 样例1:输入 dna1 = "AGT", dna2 = "AGCT",输出 1

    • 解释:在dna1末尾插入C得到AGCT,只需要1次插入操作。
  2. 样例2:输入 dna1 = "AACCGGTT", dna2 = "AACCTTGG",输出 4

    • 解释:需要进行4次编辑操作(替换GT,替换GT,删除G,替换TG)。
  3. 样例3:输入 dna1 = "ACGT", dna2 = "TGC",输出 3

    • 解释:需要进行3次编辑操作(删除A,替换CG,删除G)。
  4. 样例4:输入 dna1 = "A", dna2 = "T",输出 1

    • 解释:替换AT,只需要1次替换操作。
  5. 样例5:输入 dna1 = "GGGG", dna2 = "TTTT",输出 4

    • 解释:需要进行4次编辑操作(替换每个GT)。

总结

通过这个问题的解决,我深刻体会到了动态规划在解决序列编辑距离问题中的应用。动态规划不仅能够有效地解决这类问题,还能提供清晰的解题思路和高效的算法实现。

这次解题经历不仅提高了我的编程能力,也加深了我对动态规划算法的理解。通过构建状态转移方程和初始化动态规划表,我们可以系统地计算出两个序列之间的编辑距离。

此外,这个问题也提醒我在解决问题时要仔细分析问题的特性,选择合适的算法和方法。动态规划作为一种强大的工具,在解决类似问题时具有很高的实用价值。