问题描述
小R正在研究DNA序列,他需要一个函数来计算将一个受损DNA序列(dna1)转换成一个未受损序列(dna2)所需的最少编辑步骤。编辑步骤包括:增加一个碱基、删除一个碱基或替换一个碱基。
测试样例
样例1:
输入:dna1 = “AGT”,dna2 = “AGCT”
输出:1
样例2:
输入:dna1 = “AACCGGTT”,dna2 = “AACCTTGG”
输出:4
样例3:
输入:dna1 = “ACGT”,dna2 = “TGC”
输出:3
样例4:
输入:dna1 = “A”,dna2 = “T”
输出:1
样例5:
输入:dna1 = “GGGG”,dna2 = “TTTT”
输出:4
法1
我们需要计算将一个DNA序列 dna1 转换成另一个DNA序列 dna2 所需的最少编辑步骤。编辑步骤包括增加、删除或替换一个碱基。
解题思路
-
动态规划:我们可以使用动态规划来解决这个问题。定义一个二维数组
dp,其中dp[i][j]表示将dna1的前i个字符转换成dna2的前j个字符所需的最少编辑步骤。 -
初始化:
dp[0][j]表示将空字符串转换成dna2的前j个字符,需要j次增加操作。dp[i][0]表示将dna1的前i个字符转换成空字符串,需要i次删除操作。
-
状态转移:
- 如果
dna1[i-1] == dna2[j-1],则dp[i][j] = dp[i-1][j-1],因为不需要任何编辑操作。 - 否则,
dp[i][j]可以通过以下三种操作之一得到:- 增加:
dp[i][j-1] + 1 - 删除:
dp[i-1][j] + 1 - 替换:
dp[i-1][j-1] + 1
- 增加:
- 取这三种操作的最小值作为
dp[i][j]。
- 如果
代码
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
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], dp[i][j - 1], dp[i - 1][j - 1]) + 1
# 返回最终结果
return dp[m][n]
if __name__ == "__main__":
# 你可以添加更多测试用例
print(solution("AGCTTAGC", "AGCTAGCT") == 2)
print(solution("AGCCGAGC", "GCTAGCT") == 4)
关键步骤解释
- 初始化:
dp[i][0]和dp[0][j]分别表示将dna1的前i个字符转换成空字符串,以及将空字符串转换成dna2的前j个字符。 - 状态转移:根据
dna1[i-1]和dna2[j-1]是否相等,选择不同的编辑操作,并取最小值。
法2
综合运用了动态规划(Dynamic Programming)和字符串处理的知识,是一道典型的编辑距离(Edit Distance)问题。题目要求计算将一个受损DNA序列(dna1)转换成一个未受损序列(dna2)所需的最少编辑步骤。编辑步骤包括增加一个碱基、删除一个碱基或替换一个碱基。这是一个典型的编辑距离问题,可以通过动态规划来解决。动态规划的核心思想是将问题分解为子问题,并通过状态转移方程逐步求解。
解题思路
-
初始化:
- 创建一个二维数组
f,大小为(n+1) x (m+1),其中n和m分别是dna1和dna2的长度。f[i][j]表示将dna1的前i个字符转换为dna2的前j个字符所需的最少编辑步骤。 - 初始化边界条件:
f[i][0] = i表示将dna1的前i个字符转换为空字符串需要i次删除操作;f[0][j] = j表示将空字符串转换为dna2的前j个字符需要j次插入操作。
- 创建一个二维数组
-
状态转移:
- 对于每个
i和j,计算f[i][j]:- 如果
dna1[i-1] == dna2[j-1],则不需要进行替换操作,f[i][j] = f[i-1][j-1]。 - 否则,
f[i][j] = min(f[i-1][j] + 1, f[i][j-1] + 1, f[i-1][j-1] + 1),分别对应删除、插入和替换操作。
- 如果
- 对于每个
-
结果:
- 最终结果为
f[n][m],即dna1转换为dna2所需的最少编辑步骤。
- 最终结果为
- 时间复杂度:
O
(
n
×
m
)
O(n \times m)
O(n×m),其中
n和m分别是dna1和dna2的长度。需要填充一个(n+1) x (m+1)的二维数组。 - 空间复杂度:
O
(
n
×
m
)
O(n \times m)
O(n×m),用于存储动态规划表
f。
代码
def solution(dna1: str, dna2: str) -> int:
n = len(dna1)
m = len(dna2)
if n * m == 0:
return n + m
f = [[0] * (m + 1) for _ in range(n + 1)]
for i in range(n + 1):
f[i][0] = i
for j in range(m + 1):
f[0][j] = j
for i in range(1, n + 1):
for j in range(1, m + 1):
f[i][j] = min(f[i - 1][j] + 1, f[i][j - 1] + 1, f[i - 1][j - 1] + (dna1[i - 1] != dna2[j - 1]))
return f[n][m]
if __name__ == '__main__':
print(solution(dna1 = "AGT",dna2 = "AGCT") == 1)
print(solution(dna1 = "AACCGGTT",dna2 = "AACCTTGG") == 4)
print(solution(dna1 = "ACGT",dna2 = "TGC") == 3)
print(solution(dna1 = "A",dna2 = "T") == 1)
print(solution(dna1 = "GGGG",dna2 = "TTTT") == 4)
4981

被折叠的 条评论
为什么被折叠?



