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

81 阅读3分钟

问题背景

DNA序列比对是生物信息学的重要课题。一个常见问题是如何计算两个DNA序列的最小编辑距离,即从一个字符串转化为另一个字符串所需的最少操作数。这些操作包括插入、删除和替换字符。

在这篇文章中,我们将通过一道典型的编辑距离问题,探讨动态规划在字符串问题中的应用,并结合代码实现和实际分析,深入理解其解题思路和优化方法。

问题描述

我们需要计算两个DNA序列 dna1dna2 的编辑距离,支持以下三种操作:

  • 插入一个字符;
  • 删除一个字符;
  • 替换一个字符。

示例:

  1. 输入:dna1 = "AGCT", dna2 = "TG"
    输出:3
    解析:需要 3 步操作完成转化,例如 "AGCT" -> "GCT" -> "GT" -> "TG"
  2. 输入:dna1 = "AACCGGT", dna2 = "AACCTTG"
    输出:4
    解析:两次替换操作即可将序列匹配。

解题思路

这道题典型的解法是动态规划(Dynamic Programming, DP)。动态规划通过定义状态和转移方程,将问题分解为更小的子问题,逐步递推得到最终答案。

1. 状态定义

设二维数组 dp[i][j] 表示将 dna1 的前 i 个字符转换为 dna2 的前 j 个字符所需的最少编辑步数。

2. 初始化

  • i=0 时,dp[0][j] = j,因为将空字符串转换为长度为 j 的字符串需要插入 j 个字符。
  • j=0 时,dp[i][0] = i,因为将长度为 i 的字符串转换为空字符串需要删除 i 个字符。

3. 状态转移方程

  • 如果 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[i][j] = \min(dp[i-1][j] + 1, dp[i][j-1] + 1, dp[i-1][j-1] + 1)dp[i][j]=min(dp[i−1][j]+1,dp[i][j−1]+1,dp[i−1][j−1]+1)

4. 结果

dp[m][n] 即为从 dna1 转化为 dna2 的最少编辑步数,其中 mn 分别是两个字符串的长度。


代码实现

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

    # 填充 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] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + 1)

    return dp[m][n]

# 测试案例
print(solution("AGCT", "TG"))  # 输出: 3
print(solution("AACCGGT", "AACCTTG"))  # 输出: 4

优化思考

1. 空间优化

当前代码使用二维数组存储 DP 状态。实际上,只需存储当前行和上一行的状态即可优化空间复杂度至 O(n)O(n)O(n)。

2. 其他拓展

编辑距离的变种包括:

  • 最长公共子序列(LCS) :在编辑距离中,只考虑插入和删除操作。
  • 拼写检查器:基于编辑距离判断两个单词的相似度。

学习建议:

  1. 在刷题时,注重总结动态规划问题的状态定义和递推关系。
  2. 利用 AI 工具(如 MarsCode)进行代码分析和调试,弥补薄弱点。

希望这篇文章能为大家的学习提供帮助,期待与大家交流更多心得!