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

132 阅读4分钟

问题描述

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

测试样例

示例一:

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

输出:1

示例二:

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

输出:4

示例三:

输入:dna1 = "ACGT",dna2 = "TGC"

输出:3

示例四:

输入:dna1 = "A",dna2 = "T"

输出:1

示例五:

输入:dna1 = "GGGG",dna2 = "TTTT"

输出:4

解题思路:

编辑距离问题是一个典型的动态规划问题,其核心在于如何定义状态以及状态之间的关系。以下是详细的思路解析:

  1. 定义状态:我们使用一个二维数组 dp 来存储状态,其中 dp[i][j] 表示将字符串 dna1 的前 i 个字符转换为字符串 dna2 的前 j 个字符所需的最少编辑操作数。

  2. 状态初始化dp[0][j] 表示将空字符串转换为 dna2 的前 j 个字符所需的操作数,这显然是 j 次插入操作。同理,dp[i][0] 表示将 dna1 的前 i 个字符转换为空字符串所需的操作数,这显然是 i 次删除操作。

  3. 状态转移:对于 dp[i][j] 的计算,我们考虑以下三种情况:

    • 字符相同:如果 dna1[i-1] == dna2[j-1],则不需要额外操作,dp[i][j] = dp[i-1][j-1]

    • 字符不同:如果字符不同,我们需要考虑三种操作:

      • 删除:删除 dna1 的第 i 个字符,操作数为 dp[i-1][j] + 1
      • 插入:在 dna1 的第 i 个位置插入一个字符,操作数为 dp[i][j-1] + 1
      • 替换:将 dna1 的第 i 个字符替换为 dna2 的第 j 个字符,操作数为 dp[i-1][j-1] + 1
    • 我们选择这三种操作中操作数最小的作为 dp[i][j] 的值。

  4. 填充DP表:通过迭代的方式,根据上述状态转移方程,填充整个 dp 表。

  5. 获取结果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 = [[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]
    return -2

if __name__ == "__main__":
    #  You can add more test cases here
    print(solution("AGCTTAGC", "AGCTAGCT") == 2 )
    print(solution("AGCCGAGC", "GCTAGCT") == 4)

核心知识

  1. 动态规划(DP) :是一种通过将复杂问题分解为更简单的子问题来解决的方法。它通过存储子问题的解来避免重复计算,从而提高效率。

  2. 状态定义:在动态规划中,状态的定义是关键。在这个问题中,状态 dp[i][j] 表示将两个字符串的前 ij 个字符转换所需的最少编辑操作数。

  3. 状态转移方程:状态转移方程定义了如何从已知状态推导出新状态。在这个问题中,状态转移方程考虑了删除、插入和替换三种操作。

  4. 边界条件:正确处理DP数组的边界条件是解决问题的关键。在这个问题中,第一行和第一列分别处理了将一个字符串转换为空字符串和将空字符串转换为另一个字符串的情况。

  5. 时间复杂度分析:这个问题的时间复杂度为 O(m*n),其中 mn 分别是两个字符串的长度。这是因为我们需要填充一个 (m+1) x (n+1) 的DP表。

总结

编辑距离问题是一个经典的动态规划问题,它展示了如何通过定义状态和状态转移方程来解决问题。通过初始化DP表的边界条件,然后迭代填充整个表,我们可以高效地找到问题的解。这种方法不仅适用于字符串编辑距离问题,也可以推广到其他需要最小代价或最优解的问题。

使用豆包marscode AI的优势

  • AI可以帮助规范代码风格,确保团队中的代码风格一致性,提高团队协作的效率。

  • AI可以检查代码中的安全漏洞,提供安全编码的建议,帮助开发者编写更安全的代码。

  • MarsCode AI可以自动生成测试用例,帮助开发者对代码进行测试,确保算法在各种情况下都能正常工作。