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

47 阅读4分钟

问题描述

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


测试样例

样例1:

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

样例2:

输入:dna1 = "AACCGGTT",dna2 = "AACCTTGG"
输出:4

解题思路

运用动态规划思想解题

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

  2. 初始化

    • dp[0][j] 表示将空字符串转换成 dna2 的前 j 个字符,显然需要 j 次插入操作。
    • dp[i][0] 表示将 dna1 的前 i 个字符转换成空字符串,显然需要 i 次删除操作。
  3. 状态转移

    • 如果 dna1[i-1] == dna2[j-1],则 dp[i][j] = dp[i-1][j-1],因为不需要任何编辑操作。

    • 否则,dp[i][j] 可以通过以下三种操作之一得到:

      • 插入:dp[i][j-1] + 1
      • 删除:dp[i-1][j] + 1
      • 替换:dp[i-1][j-1] + 1
    • 因此,dp[i][j] = min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1]) + 1

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

代码实现

def solution(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)

知识点总结

1. 动态规划(Dynamic Programming)

动态规划是一种通过将复杂问题分解为更简单的子问题来解决的方法。它通常用于优化问题,其中子问题的解可以被存储和重用。

关键概念:

  • 状态定义:定义一个状态表示问题的某个子状态。
  • 状态转移方程:描述如何从一个状态转移到另一个状态。
  • 初始化:确定初始状态的值。
  • 最终状态:问题的最终解通常是某个状态的值。

拓展:

  • 背包问题:0-1背包问题、完全背包问题等。
  • 最长公共子序列(LCS) :与编辑距离问题类似,但只考虑不改变顺序的子序列。
  • 最长递增子序列(LIS) :找到一个序列中最长的递增子序列。

2. 编辑距离(Edit Distance)

编辑距离是指将一个字符串转换为另一个字符串所需的最少编辑操作次数。编辑操作包括插入、删除和替换。

关键概念:

  • 插入:在字符串中插入一个字符。
  • 删除:从字符串中删除一个字符。
  • 替换:将字符串中的一个字符替换为另一个字符。

拓展:

  • Levenshtein距离:计算两个字符串之间的编辑距离。
  • Damerau-Levenshtein距离:在Levenshtein距离的基础上,增加了交换相邻字符的操作。

3. 二维数组

二维数组是动态规划中常用的数据结构,用于存储子问题的解。

关键概念:

  • 行和列:二维数组的行和列分别表示问题的不同维度。
  • 初始化:初始化二维数组的边界条件。
  • 填充:根据状态转移方程填充二维数组。

拓展:

  • 多维数组:在更复杂的问题中,可能需要使用三维或更高维的数组。
  • 滚动数组:在某些情况下,可以通过滚动数组来优化空间复杂度。

4. 字符串操作

字符串操作包括插入、删除、替换等操作,这些操作在编辑距离问题中非常重要。

关键概念:

  • 字符串比较:比较两个字符串的字符是否相等。
  • 字符串长度:获取字符串的长度。

拓展:

  • 正则表达式:用于更复杂的字符串匹配和替换操作。
  • KMP算法:用于字符串匹配的高效算法。