解题分析与总结:豆包MarsCode AI刷题
题目解析
今天我们来解答一道经典的动态规划问题——DNA序列转换问题。该问题描述了如何通过最少的编辑步骤将一个受损的DNA序列(dna1)转换为一个未受损的DNA序列(dna2)。每个编辑步骤可以是插入、删除或替换一个碱基。
问题描述:
-
给定两个DNA序列
dna1和dna2,我们需要计算将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,其中 dp[i][j] 表示将 dna1[0..i-1] 转换为 dna2[0..j-1] 的最少编辑距离。
-
状态转移:
- 插入操作: 如果我们需要将
dna2[j-1]插入到dna1中,那么dp[i][j] = dp[i][j-1] + 1。 - 删除操作: 如果我们需要从
dna1[i-1]中删除一个碱基,那么dp[i][j] = dp[i-1][j] + 1。 - 替换操作: 如果
dna1[i-1] != dna2[j-1],我们需要进行替换,dp[i][j] = dp[i-1][j-1] + 1;如果dna1[i-1] == dna2[j-1],则不需要进行替换,dp[i][j] = dp[i-1][j-1]。
- 插入操作: 如果我们需要将
-
初始化:
dp[i][0] = i,表示将dna1[0..i-1]转换为空序列需要进行i次删除操作。dp[0][j] = j,表示将空序列转换为dna2[0..j-1]需要进行j次插入操作。
代码实现
基于上述思路,我们可以写出如下的代码实现:
java
复制代码
public class Main {
public static int solution(String dna1, String dna2) {
int n = dna1.length();
int m = dna2.length();
int[][] dp = new int[n + 1][m + 1];
// 初始化边界条件
for (int i = 1; i <= n; i++) dp[i][0] = i;
for (int j = 1; j <= m; j++) dp[0][j] = j;
// 填充dp表
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
// 插入、删除、替换操作的状态转移
dp[i][j] = Math.min(dp[i-1][j-1] + 1, Math.min(dp[i][j-1] + 1, dp[i-1][j] + 1));
// 如果字符相同,则不需要替换
if (dna1.charAt(i-1) == dna2.charAt(j-1)) {
dp[i][j] = Math.min(dp[i-1][j-1], dp[i][j]);
}
}
}
// 返回最终结果,即将dna1转换为dna2的最少编辑步骤
return dp[n][m];
}
public static void main(String[] args) {
System.out.println(solution("AGT", "AGCT") == 1); // 1次操作
System.out.println(solution("AACCGGTT", "AACCTTGG") == 4); // 4次操作
System.out.println(solution("ACGT", "TGC") == 3); // 3次操作
System.out.println(solution("A", "T") == 1); // 1次操作
System.out.println(solution("GGGG", "TTTT") == 4); // 4次操作
}
}
代码详解
-
初始化二维数组
dp:
我们首先初始化了dp数组的边界:dp[i][0] = i,表示将dna1[0..i-1]转换为空序列所需的操作次数是i(即需要删除i次)。dp[0][j] = j,表示将空序列转换为dna2[0..j-1]所需的操作次数是j(即需要插入j次)。
-
动态规划递推:
通过双重循环遍历dp[i][j],对于每个位置,我们考虑:- 插入操作:
dp[i][j-1] + 1 - 删除操作:
dp[i-1][j] + 1 - 替换操作:
dp[i-1][j-1] + 1 - 如果当前字符相等,则不需要替换:
dp[i-1][j-1]
- 插入操作:
-
最终结果:
最终,dp[n][m]就是将dna1转换为dna2所需的最少编辑步骤。
知识总结与学习建议
在这道题目中,我们主要用到了动态规划的思想,特别是在计算字符串之间的最小编辑距离时。通过构造一个二维的状态转移表,逐步解决问题。以下是几点总结:
- 动态规划的状态定义与转移:
动态规划的核心是定义状态和转移方程。此题中,我们通过dp[i][j]来表示dna1[0..i-1]和dna2[0..j-1]的最小编辑距离,并根据插入、删除、替换操作的情况来填充dp表。 - 从基础到进阶:
动态规划问题往往从简单的子问题开始逐步解决。理解每一步的状态转移非常重要,要清楚每一步是如何从前一步推导过来的。
学习计划与方法
结合豆包MarsCode AI的刷题功能,可以采取以下学习策略:
- 分阶段刷题: 从简单的动态规划问题开始,逐步增加难度。例如,先从计算两个字符串的编辑距离开始,再逐步挑战更复杂的动态规划题目。
- 错题复习: 利用错题本功能,回顾自己在理解动态规划问题时的难点,分析错误原因,并加强相应的知识点。
- 结合参考资料: 在进行动态规划刷题时,可以结合其他学习资料,如算法书籍或视频教程,加深对状态转移方程的理解。
通过这种方式,可以在短时间内掌握动态规划的核心思想和应用技巧。
结语
这道DNA序列编辑问题不仅考察了我们对动态规划的掌握程度,还锻炼了我们如何通过状态转移方程来解决实际问题。通过不断的练习和总结,我们可以将动态规划运用自如,为解决更多复杂的算法问题打下坚实的基础。