DNA序列编辑距离
问题描述
小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
分析题目
你需要计算将一个受损DNA序列(dna1)转换成一个未受损序列(dna2)所需的最少编辑步骤。编辑步骤包括:
- 增加一个碱基
- 删除一个碱基
- 替换一个碱基
- 由于我们需要计算最小编辑距离,动态规划(Dynamic Programming, DP)是一个合适的选择。我们可以使用一个二维数组
dp来存储中间结果。
算法设计
-
初始化:
- 如果
dna1为空,那么将dna1转换为dna2需要dna2.length()次增加操作。 - 如果
dna2为空,那么将dna1转换为dna2需要dna1.length()次删除操作。
- 如果
-
状态转移:
-
对于
dp[i][j],表示将dna1的前i个字符转换为dna2的前j个字符所需的最少编辑步骤。 -
如果
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-1][j] + 1 - 增加:
dp[i][j-1] + 1
- 替换:
-
取这三种操作的最小值。
-
最终结果:
dp[m][n] 即为将 dna1 转换为 dna2 所需的最少编辑步骤,其中 m 和 n 分别是 dna1 和 dna2 的长度
- 时间复杂度:
O(m * n) - 空间复杂度:
O(m * n)
具体代码
``
`public class Main {
public static int solution(String dna1, String dna2) {
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;
}
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;
}
}
}
return dp[m][n];
}
public static void main(String[] args) {
System.out.println(solution("AGT", "AGCT"));
System.out.println(solution("AACCGGTT", "AACCTTGG"));
System.out.println(solution("ACGT", "TGC"));
System.out.println(solution("A", "T"));
System.out.println(solution("GGGG", "TTTT"));
}
}`
注意事项
好的,让我们总结一下在解决这个题目时需要注意的事项:
1. 边界条件
-
空字符串:
- 如果
dna1为空,那么将dna1转换为dna2需要dna2.length()次增加操作。 - 如果
dna2为空,那么将dna1转换为dna2需要dna1.length()次删除操作。
- 如果
2. 动态规划数组的初始化
-
初始化第一行和第一列:
dp[i][0]表示将dna1的前i个字符转换为空字符串所需的操作数,因此dp[i][0] = i。dp[0][j]表示将空字符串转换为dna2的前j个字符所需的操作数,因此dp[0][j] = j。
3. 状态转移
-
字符相等:
- 如果
dna1[i-1] == dna2[j-1],那么dp[i][j] = dp[i-1][j-1],因为不需要任何编辑操作。
- 如果
-
字符不相等:
-
如果
dna1[i-1] != dna2[j-1],那么dp[i][j]可以通过以下三种操作之一得到:- 替换:
dp[i-1][j-1] + 1 - 删除:
dp[i-1][j] + 1 - 增加:
dp[i][j-1] + 1
- 替换:
-
取这三种操作的最小值。
-
4. 最终结果
-
返回值:
dp[m][n]即为将dna1转换为dna2所需的最少编辑步骤,其中m和n分别是dna1和dna2的长度。
5. 代码实现细节
-
数组索引:
- 注意
dp数组的索引是从0到m和0到n,而字符串的索引是从0到m-1和0到n-1。
- 注意
-
字符比较:
- 使用
charAt方法比较字符时,注意索引的偏移(i-1和j-1)。
- 使用