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

30 阅读3分钟

问题描述

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


测试样例

样例1:

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

样例2:

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

样例3:

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

题目解析:

此问题本质是一个经典的编辑距离问题。

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

对于每个位置,可以选择:

  1. 增加:使得 dna1 增加一个字符。
  2. 删除:使得 dna1 删除一个字符。
  3. 替换:替换掉当前的字符。

思路分析:

动态规划状态转移

  1. 边界条件:
  • 如果 dna1 为空,只能通过增加操作将其变为 dna2,所需步数为 len(dna2)。
  • 如果 dna2 为空,只能通过删除操作将 dna1 清空,所需步数为 len(dna1)。
  1. 状态转移方程:
  • 如果 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] + 1, dp[i-1][j] + 1, dp[i][j-1] + 1)
    其中:
    dp[i-1][j-1] + 1 表示替换。
    dp[i-1][j] + 1 表示删除。
    dp[i][j-1] + 1 表示增加。

解决方案:

代码实现:

def solution(dna1, dna2):
    # 获取两段 DNA 序列的长度
    n, m = len(dna1), len(dna2)
    
    # 初始化 DP 表
    dp = [[0] * (m + 1) for _ in range(n + 1)]
    
    # 边界条件
    for i in range(n + 1):
        dp[i][0] = i  # 删除操作
    for j in range(m + 1):
        dp[0][j] = j  # 增加操作
    
    # 状态转移
    for i in range(1, n + 1):
        for j in range(1, m + 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[n][m]

代码解释:

  1. 输入处理

    • 通过 len(dna1)len(dna2) 获取 DNA 序列长度。
    • 初始化一个大小为 (n+1) x (m+1) 的二维数组 dp,存储每个子问题的结果。
  2. 边界初始化

    • dp[i][0] = i 表示将 dna1 的前 i 个字符清空所需的删除次数。
    • dp[0][j] = j 表示将空串扩展为 dna2 的前 j 个字符所需的增加次数。
  3. 动态规划计算

    • 遍历所有可能的子问题,通过状态转移方程更新 dp[i][j]
    • 确保选择替换、增加或删除中的最小值。
  4. 返回结果

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

示例测试:

  1. 样例1

    print(solution("AGCCGAGC", "GCTAGCT")) # 输出 4
    
  2. 样例2

    print(solution("AGCTTAGC", "AGCTAGCT"))# 输出 2
    

时间复杂度:

  • 状态转移需要遍历每对字符组合O(mn)O(mn),其中nnmm是两个 DNA 序列的长度。

空间复杂度:

  • 存储二维数组O(mn)O(mn)

总结

该算法通过动态规划解决了编辑距离问题:

  1. 优势:时间和空间复杂度适中,适合处理中等规模的 DNA 序列。

  2. 扩展性:可以通过压缩 DP 表降维,进一步优化空间复杂度。