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
- 输入:
解题思路
本题的目标是计算编辑距离,我们可以使用动态规划的方法来实现。
-
创建动态规划数组:使用二维数组
dp来存储编辑距离,dp[i][j]表示将dna1的前i个字符转换为dna2的前j个字符所需的最小编辑步骤。 -
初始化边界条件:
- 如果
dna1长度为i,则删除i个字符的操作需要i步。 - 如果
dna2长度为j,则插入j个字符的操作需要j步。
- 如果
-
状态转移:
- 如果当前字符相同,则
dp[i][j]等于dp[i-1][j-1]。 - 如果不同,考虑三种操作(删除、插入、替换),选择其中的最小值。
- 如果当前字符相同,则
-
返回结果:最终返回
dp[m][n],即完整序列的编辑距离。
代码实现与详解
以下是完整的Java实现代码,计算dna1到dna2的编辑距离:
java
复制代码
public class Main {
public static int minDistance(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;
}
// 填充 dp 数组
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(Math.min(dp[i - 1][j] + 1,
dp[i][j - 1] + 1),
dp[i - 1][j - 1] + 1);
}
}
}
return dp[m][n];
}
public static void main(String[] args) {
System.out.println(minDistance("AGT", "AGCT")); // 输出 1
System.out.println(minDistance("AACCGGTT", "AACCTTGG")); // 输出 4
System.out.println(minDistance("ACGT", "TGC")); // 输出 3
System.out.println(minDistance("A", "T")); // 输出 1
System.out.println(minDistance("GGGG", "TTTT")); // 输出 4
}
}
代码详解
- 动态规划数组:
dp用来记录转换的最小步数。 - 边界初始化:确保在转换到空字符串时的编辑步数被正确记录。
- 状态转移逻辑:通过判断字符相同或不同,来更新
dp数组的值。 - 返回编辑距离:最终返回的结果是完成整个转换所需的最小步数。
知识总结
- 动态规划:理解状态转移的逻辑是关键,特别是在处理复杂问题时。
- 边界条件:初始条件的设定影响整个动态规划的结果,必须仔细处理。
- 性能优化:在进行字符串处理时,使用合适的数据结构和算法可以有效提升程序的性能。
算法复杂度
-
时间复杂度
-
初始化边界条件:
- 对于长度为
m的dna1和长度为n的dna2,我们需要初始化dp数组的第一行和第一列,因此这部分的时间复杂度为 O(m + n)。
- 对于长度为
-
动态规划数组填充:
- 在双重循环中,外层循环遍历
dna1的每个字符(m次),内层循环遍历dna2的每个字符(n次)。对于每一对字符,我们都在 O(1) 的时间内进行比较和更新dp数组。因此,这部分的时间复杂度为 O(m * n)。
- 在双重循环中,外层循环遍历
综上,整个算法的时间复杂度为: O(m⋅n)
-
空间复杂度
- 我们使用了一个大小为
(m + 1) x (n + 1)的二维数组dp来存储编辑距离,因此空间复杂度为: O(m⋅n)