问题描述
在古生物学的研究中,比较物种间 DNA 序列的差异可以用来分析血缘关系。小小是一位古生物学家,她通过对比 DNA 序列(由字符 A、C、G、T 组成)的差异来衡量物种间的亲疏远近关系。两条 DNA 序列间的差异可以通过以下三种操作来定义:
• 插入:在序列中增加一个字符;
• 删除:从序列中移除一个字符;
• 替换:将序列中的一个字符替换为另一个字符。
两条 DNA 序列间的最小变异次数(即需要的最小编辑操作数)越小,表示两者血缘关系越接近。我们的任务是实现一个算法,计算两条 DNA 序列之间的最小变异次数。
问题建模
这个问题可以用经典的 编辑距离(Edit Distance) 动态规划算法来解决。动态规划的核心是构造一个二维数组 dp,其中 dp[i][j] 表示将 dna1 的前 i 个字符转换为 dna2 的前 j 个字符所需的最小编辑操作数。
状态转移方程:
• 如果 dna1[i-1] == dna2[j-1],则 dp[i][j] = dp[i-1][j-1],因为当前字符相等,无需额外操作;
• 否则,取以下三种操作的最小值:
1. 插入:dp[i][j-1] + 1
2. 删除:dp[i-1][j] + 1
3. 替换:dp[i-1][j-1] + 1
边界条件:
• dp[0][j] = j:将空字符串转换为 dna2 的前 j 个字符需要插入 j 次;
• dp[i][0] = i:将 dna1 的前 i 个字符转换为空字符串需要删除 i 次。
算法实现
以下是该问题的 Python 代码实现:
测试结果分析
• 样例 1:
• 输入:dna1 = "AGT", dna2 = "AGCT"
• 输出:1
• 分析:只需要在 "AGT" 末尾插入一个字符 'C'。
• 样例 2:
• 输入:dna1 = "AACCGGTT", dna2 = "AACCTTGG"
• 输出:4
• 分析:需要替换 'G' -> 'T' 两次,并交换 'T' 和 'G' 的位置,总共4次操作。
• 样例 3:
• 输入:dna1 = "ACGT", dna2 = "TGC"
• 输出:3
• 分析:需要删除 'A',替换 'C' -> 'G',以及替换 'T' -> 'C'。
• 样例 4:
• 输入:dna1 = "", dna2 = "ACGT"
• 输出:4
• 分析:需要插入 'A', 'C', 'G', 'T',共4次操作。
复杂度分析
• 时间复杂度:O(m * n),其中 m 和 n 分别是 dna1 和 dna2 的长度。动态规划需要遍历整个二维表。
• 空间复杂度:O(m * n),用于存储动态规划表。
总结
本题通过经典的动态规划方法解决编辑距离问题,有着广泛的应用场景,例如 DNA 序列比对、拼写纠错和文本比较等。该算法逻辑清晰且高效,非常适合作为学习动态规划的入门案例。在实际应用中,可以通过优化空间复杂度进一步提升性能,例如使用滚动数组代替二维数组存储。