代码思路解析
问题背景
给定两个DNA序列dna1和dna2,我们需要计算将dna1转换为dna2所需的最小操作次数。允许的操作包括插入一个字符、删除一个字符和替换一个字符。
解决方案
这个问题可以通过动态规划(Dynamic Programming, DP)来解决。动态规划的核心思想是将大问题分解为小问题,并通过存储中间结果来避免重复计算。
具体步骤
-
定义状态:
- 定义一个二维数组
dp,其中dp[i][j]表示将dna1的前i个字符转换为dna2的前j个字符所需的最小操作次数。
- 定义一个二维数组
-
初始化边界条件:
dp[i][0]表示将dna1的前i个字符转换为空字符串,需要i次删除操作。dp[0][j]表示将空字符串转换为dna2的前j个字符,需要j次插入操作。
-
状态转移方程:
-
如果
dna1[i-1] == dna2[j-1],即当前字符相同,不需要任何操作,直接继承之前的操作次数:dp[i][j] = dp[i-1][j-1]。 -
如果
dna1[i-1] != dna2[j-1],需要考虑三种操作:- 插入:
dp[i][j-1] + 1 - 删除:
dp[i-1][j] + 1 - 替换:
dp[i-1][j-1] + 1
- 插入:
-
选择上述三种操作中的最小值作为
dp[i][j]的值。
-
-
返回结果:
- 最终结果存储在
dp[m][n]中,即dp数组的最后一个元素,表示将整个dna1转换为整个dna2所需的最小操作次数。
- 最终结果存储在
代码实现
python
深色版本
def min_operations_to_restore_dna(dna1, dna2):
# 获取两个DNA序列的长度
m, n = len(dna1), len(dna2)
# 创建一个二维数组dp,dp[i][j]代表将dna1的前i个字符转换成dna2的前j个字符所需的最小操作次数
dp = [[0] * (n + 1) for _ in range(m + 1)]
# 初始化dp数组
for i in range(m + 1):
dp[i][0] = i # 将dna1的前i个字符转换为空字符串,需要i次删除操作
for j in range(n + 1):
dp[0][j] = j # 将空字符串转换为dna2的前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:
# 取插入、删除、替换操作中的最小值,并加1(代表当前这次操作)
dp[i][j] = min(dp[i - 1][j] + 1, # 删除dna1的一个字符
dp[i][j - 1] + 1, # 在dna1中插入一个字符
dp[i - 1][j - 1] + 1 # 替换dna1的一个字符
)
# dp[m][n]即将dna1转换为dna2所需的最小操作次数
return dp[m][n]
# 验证示例
def test_examples():
# 示例1
dna1_example1 = "AGCTTAGC"
dna2_example1 = "AGCTAGCT"
print(f"示例1输入:")
print(f"dna1: {dna1_example1}")
print(f"dna2: {dna2_example1}")
print(f"输出: {min_operations_to_restore_dna(dna1_example1, dna2_example1)} (期望输出: 2)\n")
# 示例2
dna1_example2 = "AGCCGAGC"
dna2_example2 = "GCTAGCT"
print(f"示例2输入:")
print(f"dna1: {dna1_example2}")
print(f"dna2: {dna2_example2}")
print(f"输出: {min_operations_to_restore_dna(dna1_example2, dna2_example2)} (期望输出: 4)\n")
# 调用测试函数
test_examples()