问题描述
小R正在研究DNA序列,他需要一个函数来计算将一个受损DNA序列(dna1)转换成一个未受损序列(dna2)所需的最少编辑步骤。编辑步骤包括:增加一个碱基、删除一个碱基或替换一个碱基。
测试样例
样例1:
输入:
dna1 = "AGT",dna2 = "AGCT"
输出:1
解题思路
根据问题,我们可以通过动态规划的方法来解决。核心思路是构建一个二维数组 dp,其中 dp[i][j] 表示将 dna1 的前 i 个字符转换为 dna2 的前 j 个字符所需的最少编辑步骤。通过逐步比较两个 DNA 序列的字符,根据不同情况来更新这个二维数组中的值,最终得到将整个 dna1 转换为 dna2 所需的最少编辑次数,也就是 dp[m][n] 的值(m 和 n 分别是 dna1 和 dna2 的长度)。
所以全题我们可以分为三步完成。
1.初始化二维数组的边界情况:
当 dna2 为空字符串时(即 j = 0),要将 dna1 的前 i 个字符转换为空字符串,就需要删除 i 个字符,所以 dp[i][0] = i,通过循环 for (int i = 0; i <= m; i++) 来初始化第一列的值。
同理,当 dna1 为空字符串时(即 i = 0),要将空字符串转换为 dna2 的前 j 个字符,就需要插入 j 个字符,所以 dp[0][j] = j,通过循环 for (int j = 0; j <= n; j++) 来初始化第一行的值。
2.填充二维数组的中间部分(动态规划过程) :
通过两层嵌套循环 for (int i = 1; i <= m; i++) 和 for (int j = 1; j <= n; j++) 来遍历 dna1 和 dna2 的每个字符位置(这里的索引要减 1,因为字符串索引从 0 开始,而 dp 数组的行列从 1 开始计数是为了方便处理边界情况)。
比较 dna1 中当前位置(i - 1)的字符和 dna2 中当前位置(j - 1)的字符:
如果两个字符相等,那么 dp[i][j] 的值就等于 dp[i - 1][j - 1],意思是不需要进行编辑操作,其最少编辑步骤和去掉这两个相等字符之前的情况一样。
如果两个字符不相等,就需要选择插入、删除或替换操作中的最优(即编辑次数最少)操作来更新 dp[i][j] 的值。这里通过取 Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1 来实现,dp[i - 1][j] 表示删除操作(在 dna1 中去掉当前字符),dp[i][j - 1] 表示插入操作(在 dna1 中对应位置插入 dna2 当前字符),dp[i - 1][j - 1] 表示替换操作(将 dna1 当前字符替换为 dna2 当前字符),取这三种情况中的最小值再加上 1(因为进行了一次编辑操作)作为 dp[i][j] 的新值。
3.得到最终结果:
最后返回 dp[m][n],这个值就是将整个 dna1 序列转换为 dna2 序列所需的最少编辑步骤。
主要代码为:
通过上述动态规划算法实现的 solution 函数,可以有效地计算出将一个受损 DNA 序列(dna1)转换成一个未受损序列(dna2)所需的最少编辑步骤。利用二维数组保存中间结果,通过逐步比较和合理选择编辑操作来更新数组元素值,最终得到整体的最少编辑次数。这种方法在此类字符串编辑距离类型问题的解决上十分高效,在很多类似需要衡量两个序列差异及转换代价的场景中都有广泛应用。