DNA序列编辑距离——思路解析

44 阅读4分钟

原题地址:DNA序列编辑距离 - MarsCode

问题描述

小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

步骤一:初始化相关变量及边界条件

收起

java

复制

int m = dna1.length();
int n = dna2.length();

int[][] dp = new int[m + 1][n + 1];
for (int i = 0; i <= m; i++) {
    dp[i][0] = i;
}
for (int j = 0; j <= n; j++) {
    dp[0][j] = j;
}
  • 首先获取两个输入 DNA 序列字符串 dna1 和 dna2 的长度,分别赋值给变量 m 和 n

  • 接着创建了一个二维数组 dp,其维度为 (m + 1)×(n + 1)。这个 dp 数组用于存储动态规划过程中的中间结果,其中 dp[i][j] 表示将 dna1 的前 i 个字符转换为 dna2 的前 j 个字符所需要的最少编辑次数。

  • 然后通过两个 for 循环来初始化 dp 数组的边界条件:

    • 当 dna2 的长度为 0(也就是 j = 0)时,要将 dna1 的前 i 个字符转换为 dna2(空字符串),唯一的操作就是删除 dna1 中的这 i 个字符,所以 dp[i][0] = i,即需要进行 i 次删除操作。
    • 同理,当 dna1 的长度为 0(i = 0)时,要将 dna1(空字符串)转换为 dna2 的前 j 个字符,就需要进行 j 次插入操作,所以 dp[0][j] = j

步骤二:填充动态规划数组

收起

java

复制

for (int i = 1; i <= m; i++) {
    for (int j = 1; j <= n; j++) {
        if (dna1.charAt(i - 1) == dna2.charAt(j - 1)) {
            dp[i][j] = dp[i - 1][j - 1];
        } else {
            dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i - 1][j], dp[i][j - 1])) + 1;
        }
    }
}

这里使用了两层嵌套的 for 循环来填充 dp 数组的其余部分(除去已经初始化的边界部分),循环变量 i 和 j 分别从 1 开始遍历到 dna1 和 dna2 的长度 m 和 n。对于每个 dp[i][j]

  • 如果 dna1 的第 i 个字符(索引为 i - 1,因为字符串索引从 0 开始)和 dna2 的第 j 个字符相等,这意味着不需要进行任何编辑操作,此时将 dna1 的前 i 个字符转换为 dna2 的前 j 个字符所需的最少编辑次数,就等于将 dna1 的前 i - 1 个字符转换为 dna2 的前 j - 1 个字符所需的最少编辑次数,即 dp[i][j] = dp[i - 1][j - 1]

  • 如果这两个字符不相等,那么就需要考虑三种可能的编辑操作来使得它们相等,并选择其中操作次数最少的情况再加 1(因为进行了一次编辑操作):

    • dp[i - 1][j - 1]:表示替换操作,即将 dna1 中当前位置(第 i 个)的字符替换为 dna2 中当前位置(第 j 个)的字符,所以编辑次数基于将 dna1 的前 i - 1 个字符转换为 dna2 的前 j - 1 个字符的最少编辑次数基础上增加 1 次替换操作。

    • dp[i - 1][j]:表示删除操作,即删除 dna1 中当前位置(第 i 个)的字符,编辑次数基于将 dna1 的前 i - 1 个字符转换为 dna2 的前 j 个字符的最少编辑次数基础上增加 1 次删除操作。

    • dp[i][j - 1]:表示插入操作,即在 dna1 当前位置(第 i 个)插入一个字符使其与 dna2 中当前位置(第 j 个)的字符相等,编辑次数基于将 dna1 的前 i 个字符转换为 dna2 的前 j - 1 个字符的最少编辑次数基础上增加 1 次插入操作。

通过 Math.min 函数选取这三种情况中编辑次数最少的那种情况,然后再加 1,就得到了 dp[i][j] 的值,也就是将 dna1 的前 i 个字符转换为 dna2 的前 j 个字符所需的最少编辑次数。

步骤三:返回最终结果

收起

java

复制

return dp[m][n];

当动态规划数组 dp 填充完毕后,dp[m][n] 就表示将整个 dna1 字符串转换为整个 dna2 字符串所需要的最少编辑次数,所以直接返回这个值作为方法的最终结果。