题目解析
81 古生物序列分析 本题要求计算两条 DNA 序列之间的最小变异次数,即编辑距离(Edit Distance)。这是经典的动态规划问题,涉及三种操作:
- 插入:在其中一条 DNA 序列中插入一个字符;
- 删除:从其中一条 DNA 序列中删除一个字符;
- 替换:将其中一条 DNA 序列的字符替换为另一个字符。
解题思路
动态规划建模
设 dp[i][j] 表示将 DNA1 的前 ii 个字符与 DNA2 的前 jj 个字符匹配所需的最小变异次数。
状态转移方程
-
如果当前字符相同(即 ):
- 不需要进行操作,直接继承子问题的结果。
-
如果当前字符不同(即 :
- 替换操作:从 dp[i-1][j-1] 转移,加 1;
- 删除操作:从 dp[i-1][j] 转移,加 1;
- 插入操作:从 dp[i][j-1] 转移,加 1。
初始化
- dp[i][0] = i:将 DNA1 的前 i 个字符变为空字符串需要 i 次删除操作;
- dp[0][j] = j:将空字符串变为 DNA2 的前 j 个字符需要 j 次插入操作。
最终结果
- dp[n][m]:即将 DNA1 的前 n 个字符与 DNA2 的前 m 个字符匹配的最小变异次数。
算法实现
Python 代码
def min_dna_mutations(dna1, dna2):
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
# 填充 DP 表
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], dp[i-1][j], dp[i][j-1]) + 1
# 返回最终结果
return dp[n][m]
# 测试样例
print(min_dna_mutations("AGT", "AGCT")) # 输出: 1
print(min_dna_mutations("AACCGGTT", "AACCTTGG")) # 输出: 4
print(min_dna_mutations("ACGT", "TGC")) # 输出: 3
print(min_dna_mutations("A", "T")) # 输出: 1
print(min_dna_mutations("GGGG", "TTTT")) # 输出: 4
示例解析
示例 1
输入:dna1 = "AGT", dna2 = "AGCT"
两条 DNA 序列分别为:
- AGT
- AGCT
通过插入一个 "C" ,可以使两条序列相同。最小变异次数为 1。
示例 3
输入:dna1 = "ACGT", dna2 = "TGC"
- 替换 "A" 为 "T";
- 删除 "C";
- 删除 "T"。
最小变异次数为 3。
复杂度分析
-
时间复杂度:
- 动态规划表需要填充,其中 n 和 m 分别为两条 DNA 序列的长度。
-
空间复杂度:
- 动态规划表占用 的空间。如果使用滚动数组优化,可以将空间复杂度降为 。
总结
本题通过动态规划解决,核心在于明确状态转移和初始化边界条件。通过构建编辑距离的动态规划表,可以高效计算两条 DNA 序列之间的最小变异次数,为实际生物学研究中分析 DNA 相似性提供了理论支持。动态规划的关键在于拆解问题,将整体问题划分为可管理的子问题,并通过状态转移方程递归地解决每个子问题,最终得到全局最优解。在本题中,编辑距离的计算直接反映了两条 DNA 序列在匹配过程中的变异次数,通过插入、删除或替换操作,逐步将一条序列转变为另一条序列。该方法不仅适用于 DNA 序列,还广泛应用于文本对比、拼写纠错、字符串匹配等领域,体现了动态规划方法的普适性和高效性。分析过程清晰且结果精准,为实际应用提供了可靠的算法支持。