1. 问题理解
我们需要计算将一个受损DNA序列(dna1)转换成一个未受损序列(dna2)所需的最少编辑步骤。编辑步骤包括:
- 插入:在
dna1中插入一个碱基。 - 删除:从
dna1中删除一个碱基。 - 替换:将
dna1中的一个碱基替换为另一个碱基。
2. 动态规划的基本思想
动态规划(Dynamic Programming, DP)是一种通过将问题分解为子问题并存储子问题的解来解决复杂问题的方法。对于这个问题,我们可以使用一个二维数组 dp 来记录从 dna1 的前 i 个字符转换到 dna2 的前 j 个字符所需的最少编辑步骤。
3. 状态定义
dp[i][j]表示将dna1的前i个字符转换成dna2的前j个字符所需的最少编辑步骤。
4. 初始化
dp[i][0]表示将dna1的前i个字符转换成空字符串所需的最少编辑步骤,显然是i次删除操作。dp[0][j]表示将空字符串转换成dna2的前j个字符所需的最少编辑步骤,显然是j次插入操作。
python
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 次插入操作
5. 状态转移
-
如果
dna1[i-1] == dna2[j-1],则dp[i][j] = dp[i-1][j-1],因为不需要任何编辑操作。 -
否则,
dp[i][j]可以通过以下三种操作中的最小值来得到:- 插入:
dp[i][j-1] + 1,表示在dna1中插入一个碱基。 - 删除:
dp[i-1][j] + 1,表示从dna1中删除一个碱基。 - 替换:
dp[i-1][j-1] + 1,表示将dna1中的一个碱基替换为dna2中的对应碱基。
- 插入:
python
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], # 删
除操作
dp[i][j - 1], # 插入操
作
dp[i - 1][j - 1]) # 替换操
作
dp[i][j] +=
1 # 加上当前操
作的代价
6. 最终结果
dp[len(dna1)][len(dna2)]即为将dna1转换成dna2所需的最少编辑步骤。
python
return dp[m][n]
伪代码
python
def solution(dna1, dna2):
# 获取两个DNA序列的长度
m, n = len(dna1), len
(dna2)
# 创建一个 (m+1) x (n
+1) 的二维数组 dp
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:
dp[i][j]
= min(dp
[i - 1]
[j],
# 删除操作
dp[i][j - 1], #
插入操作
dp[i - 1][j - 1]) #
替换操作
dp[i][j]
+= 1 # 加
上当前操作的
代价
# 返回最终结果
return dp[m][n]
解释
dp[i][j]表示将dna1的前i个字符转换成dna2的前j个字符所需的最少编辑步骤。- 通过填充
dp数组,我们可以逐步计算出将dna1转换成dna2所需的最少编辑步骤。