问题描述
小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
思路
-
定义状态:
- 设
dp[i][j]表示将dna1的前i个字符转换为dna2的前j个字符所需的最少操作次数。
- 设
-
状态转移:
- 插入操作:如果我们将
dna1[0..i]转换为dna2[0..j-1],然后插入dna2[j-1],那么dp[i][j] = dp[i][j-1] + 1。 - 删除操作:如果我们将
dna1[0..i-1]转换为dna2[0..j],然后删除dna1[i-1],那么dp[i][j] = dp[i-1][j] + 1。 - 替换操作:如果
dna1[i-1]和dna2[j-1]相同,不需要操作,所以dp[i][j] = dp[i-1][j-1];如果不同,则需要进行替换操作,dp[i][j] = dp[i-1][j-1] + 1。
- 插入操作:如果我们将
-
初始化:
dp[i][0] = i:将dna1的前i个字符转换为空串需要i次删除操作。dp[0][j] = j:将空串转换为dna2的前j个字符需要j次插入操作。
-
最终结果:
dp[len(dna1)][len(dna2)]即为将dna1转换为dna2的最少操作次数。
时间复杂度与空间复杂度分析
- 时间复杂度:
O(m * n),其中m是dna1的长度,n是dna2的长度。我们需要填充一个m x n的动态规划表,对于每一个位置,我们只做常数时间的操作。因此时间复杂度为O(m * n)。 - 空间复杂度:
O(m * n),用于存储 DP 表。我们为每一对i和j计算最小编辑距离,需要一个m x n的二维数组来存储中间结果。如果希望节省空间,可以使用滚动数组来优化,将空间复杂度降至O(min(m, n))。
优化空间复杂度
对于较长的序列,如果只是关注最终结果而不需要保留整个 DP 表的中间状态,可以通过滚动数组来减少空间开销。
在每一步,只需要保存当前行和上一行的数据。因此可以将空间复杂度优化为 O(min(m, n)),从而节省内存。
总结
动态规划方法是解决编辑距离问题的经典方式,通过递归分解原问题为子问题并存储中间结果,避免重复计算,达到了高效的时间复杂度 O(m * n)。空间复杂度方面,初步实现需要 O(m * n) 空间,但可以通过优化将空间复杂度降低。