问题描述
小U是一位古生物学家,正在研究不同物种之间的血缘关系。为了分析两种古生物的血缘远近,她需要比较它们的DNA序列。DNA由四种核苷酸A、C、G、T组成,并且可能通过三种方式发生变异:添加一个核苷酸、删除一个核苷酸或替换一个核苷酸。小U认为两条DNA序列之间的最小变异次数可以反映它们之间的血缘关系:变异次数越少,血缘关系越近。
你的任务是编写一个算法,帮助小U计算两条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__":
# You can add more test cases here
print(solution("AGT", "AGCT") == 1)
print(solution("", "ACGT") == 4)
print(solution("GCTAGCAT", "ACGT") == 5)
代码解析
-
函数定义:
def solution(dna1, dna2):定义了一个名为
solution的函数,接受两个参数dna1和dna2,分别表示两个DNA序列。 -
获取长度:
m, n = len(dna1), len(dna2)获取
dna1和dna2的长度,分别存储在m和n中。 -
创建二维数组
dp:dp = [[0] * (n + 1) for _ in range(m + 1)]创建一个
(m+1) x (n+1)的二维数组dp,用于存储动态规划的状态。dp[i][j]表示dna1的前i个字符和dna2的前j个字符之间的最小编辑距离。 -
初始化
dp数组的第一行和第一列:for i in range(m + 1): dp[i][0] = i for j in range(n + 1): dp[0][j] = jdp[i][0]表示将dna1的前i个字符转换为空字符串,需要i次删除操作。dp[0][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]) + 1- 如果
dna1[i-1] == dna2[j-1],则dp[i][j] = dp[i-1][j-1],因为不需要任何编辑操作。 - 否则,
dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1,分别对应删除、插入和替换操作。
- 如果
-
返回最终结果:
return dp[m][n]返回
dp[m][n],即dna1和dna2之间的最小编辑距离。 -
测试用例:
if __name__ == "__main__": print(solution("AGT", "AGCT") == 1) print(solution("", "ACGT") == 4) print(solution("GCTAGCAT", "ACGT") == 5)在
__main__块中,添加了一些测试用例来验证代码的正确性。
总结
这段代码使用动态规划的方法来计算两个DNA序列之间的最小编辑距离。通过初始化一个二维数组 dp,并根据状态转移方程逐步填充 dp 数组,最终得到最小编辑距离。
知识点总结
-
动态规划(Dynamic Programming) :
- 概念:动态规划是一种通过将复杂问题分解为更小的子问题来解决的方法。它通常用于优化问题,其中子问题的解可以被存储和重用。
- 应用:在这个问题中,我们使用动态规划来计算两个DNA序列之间的最小编辑距离。通过构建一个二维数组
dp,我们逐步计算每个子问题的最优解,最终得到整个问题的最优解。
-
二维数组(2D Array) :
- 概念:二维数组是一个数组的数组,可以用来表示矩阵或表格形式的数据结构。
- 应用:在这个问题中,我们使用二维数组
dp来存储每个子问题的最小编辑距离。dp[i][j]表示dna1的前i个字符和dna2的前j个字符之间的最小编辑距离。
-
状态转移方程(State Transition Equation) :
-
概念:状态转移方程描述了如何从一个状态转移到另一个状态。在动态规划中,状态转移方程是解决问题的关键。
-
应用:在这个问题中,状态转移方程如下:
- 如果
dna1[i-1] == dna2[j-1],则dp[i][j] = dp[i-1][j-1]。 - 否则,
dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1。
- 如果
-
-
初始化(Initialization) :
- 概念:初始化是指在开始计算之前,为某些变量或数据结构设置初始值。
- 应用:在这个问题中,我们初始化了
dp数组的第一行和第一列。dp[i][0]和dp[0][j]分别表示将dna1的前i个字符转换为空字符串和将空字符串转换为dna2的前j个字符所需的操作次数。
-
最小值函数(min function) :
- 概念:
min函数用于返回一组值中的最小值。 - 应用:在这个问题中,我们使用
min函数来选择删除、插入和替换操作中的最小值,并加上1作为当前操作的代价。
- 概念:
-
边界条件(Boundary Conditions) :
- 概念:边界条件是指在问题的边界上需要特殊处理的情况。
- 应用:在这个问题中,边界条件包括
dp[i][0]和dp[0][j]的初始化,分别表示将dna1的前i个字符转换为空字符串和将空字符串转换为dna2的前j个字符所需的操作次数。
总结
这段代码主要使用了动态规划的思想来解决两个DNA序列之间的最小编辑距离问题。通过构建一个二维数组 dp,并使用状态转移方程逐步填充 dp 数组,最终得到最小编辑距离。涉及到的知识点包括动态规划、二维数组、状态转移方程、初始化和边界条件。
利用AI的学习方法
-
利用AI进行思路引导:
- 在刷题过程中,如果遇到难题,可以先让AI给出思路提示,帮助你理解题目。
- 如果没有思路,可以继续让AI给出更详细的代码提示,逐步完善你的代码。
-
实时分析与错误诊断:
- AI可以对开发者的答题情况进行实时分析,及时指出错误并提供详细解析,帮助快速掌握正确的解题思路和方法。
-
集成IDE功能:
- 一些AI刷题平台还集成了IDE功能,比如语法检查和代码调试,这样可以在编写代码的同时得到即时反馈。
-
逐步学习和实践:
- 拿到题目后,先让AI帮助你理解题目,然后给出初步代码,最后自己修改和完善代码,并让AI帮你检查代码是否正确。
-
扩展和优化:
- 利用AI刷题时,不仅要理解AI给出的代码,还应该尝试自己进行调试和优化,以提升编程能力。
-
善用AI作为编程助手:
- 把AI当作一个时刻在线的编程助手,用好AI可以更快更好地理解和掌握算法,提高编程能力和效率。