豆包MarsCode AI 刷题 | 25DNA序列编辑问题

164 阅读3分钟

问题描述

小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.确定dp数组以及下标的定义

dp[i][j]代表以下标i-1结尾的字符串word1,和下标j-1为结尾的字符串word2的最近编辑距离

2.确定递推公式

我们要确定在目前状态下,子字符串的编辑距离如何影响到新的字符串的编辑距离。具体来说,就是要判断word[i-1]和word[i-2]在相等或者不相等的两种情况下,该如何去计算。

假设相等,那么就不需要对字符串进行编辑操作了,也就是说dp[i][j]就等于之前的字符串的编辑距离dp[i - 1][j - 1]。

如果不相等,那么字符串就需要进行编辑操作了,我给出的操作是

dp[i][j] = min({dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]}) + 1;

意思分别是,当前的最小编辑距离dp[i][j]可能是1.由dp[i - 1][j - 1]的编辑距离,通过修改最后一位字符编辑得来的,2.dp[i - 1][j],word1删除最后一位 3.dp[i][j - 1]同理。我们需要从中选出,最小的那个编辑操作距离是哪个,最后加上当前的操作数1,就确定了当前题目的递推方式

3.dp数组如何初始化

再回顾一下dp[i][j]的定义:

dp[i][j] 表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j]

那么dp[i][0] 和 dp[0][j] 表示什么呢?

dp[i][0] :以下标i-1为结尾的字符串word1,和空字符串word2,最近编辑距离为dp[i][0]。

那么dp[i][0]就应该是i,对word1里的元素全部做删除操作,即:dp[i][0] = i;

同理dp[0][j] = j;

所以代码如下:

for (i := 0; i <= len(word1); i++) dp[i][0] = i;
for (j := 0; j <= len(word2); j++) dp[0][j] = j;

4.确定遍历顺序 从如下四个递推公式:

  • dp[i][j] = dp[i - 1][j - 1]
  • dp[i][j] = dp[i - 1][j - 1] + 1
  • dp[i][j] = dp[i][j - 1] + 1
  • dp[i][j] = dp[i - 1][j] + 1

可以看出dp[i][j]是依赖左方,上方和左上方元素的,如图:

image.png

  1. 举例自行推导

分析完毕,最后的go实现如下

var dp = make([][]int, len(word1)+1)
for i := 0; i < len(dp); i++ {
    dp[i] = make([]int, len(word2)+1)
    dp[i][0] = i
}
for i := 1; i < len(dp[0]); i++ {
    dp[0][i] = i
}
for i := 1; i < len(dp); i++ {
    for j := 1; j < len(dp[i]); j++ {
       dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
       if word1[i-1] == word2[j-1] {
          dp[i][j] = dp[i-1][j-1]
       }
    }
}
return dp[len(word1)][len(word2)]

当然,如果有兴趣的话可以减少空间复杂度,用一个一维数组就可以完成,这里不再继续叙述。