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)所需的最少编辑步骤,我们可以使用动态规划来解决这个问题。具体来说,可以使用**编辑距离(Levenshtein Distance)**算法来计算最少编辑步骤。
编辑距离算法的基本思想:
编辑距离是指将一个字符串转换为另一个字符串所需的最少单字符编辑操作次数。这些操作包括:
- 插入:在一个字符串中插入一个字符。
- 删除:从一个字符串中删除一个字符。
- 替换:将一个字符串中的一个字符替换为另一个字符。
动态规划解法:
我们可以使用一个二维动态规划表 dp[i][j],其中 dp[i][j] 表示将 dna1 的前 i 个字符转换为 dna2 的前 j 个字符所需的最少编辑步骤。
状态转移方程:
-
初始条件:
dp[0][j] = j:将空字符串转换为dna2的前j个字符需要j次插入操作。dp[i][0] = i:将dna1的前i个字符转换为空字符串需要i次删除操作。
-
状态转移:
- 如果
dna1[i-1] == dna2[j-1],则dp[i][j] = dp[i-1][j-1],即无需任何操作。 - 否则,
dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1,即选择替换、删除或插入操作中的最小值,然后加1。
- 如果
完整代码
#include <iostream>
#include <string>
#include<vector>
#include<math.h>
#include<algorithm>
using namespace std;
int solution(std::string dna1, std::string dna2) {
// Please write your code here
int m = dna1.length();
int n = dna2.length();
// 创建动态规划表
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
// 初始化边界条件
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[i-1] == dna2[j-1]) {
dp[i][j] = dp[i-1][j-1]; // 无需编辑
} else {
dp[i][j] = min({dp[i-1][j], dp[i][j-1], dp[i-1][j-1]}) + 1;
// 分别对应删除、插入和替换操作
}
}
}
return dp[m][n];
return -2;
}
int main() {
// You can add more test cases here
std::cout << (solution("AGCTTAGC", "AGCTAGCT") == 2) << std::endl;
std::cout << (solution("AGCCGAGC", "GCTAGCT") == 4) << std::endl;
return 0;
}
代码解释:
- 初始化动态规划表:创建一个
(m+1) x (n+1)的二维数组dp,其中m和n分别是dna1和dna2的长度。 - 初始化边界条件:设置
dp[i][0] = i和dp[0][j] = j,分别表示将dna1或dna2转换为空字符串所需的编辑步骤。 - 填充动态规划表:使用双重循环遍历
dna1和dna2的所有字符,根据状态转移方程更新dp表。 - 返回最终结果:
dp[m][n]即为将dna1转换为dna2所需的最少编辑步骤。