掘金刷题3 | 豆包MarsCode AI刷题

62 阅读3分钟

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)**算法来计算最少编辑步骤。

编辑距离算法的基本思想:

编辑距离是指将一个字符串转换为另一个字符串所需的最少单字符编辑操作次数。这些操作包括:

  1. 插入:在一个字符串中插入一个字符。
  2. 删除:从一个字符串中删除一个字符。
  3. 替换:将一个字符串中的一个字符替换为另一个字符。

动态规划解法:

我们可以使用一个二维动态规划表 dp[i][j],其中 dp[i][j] 表示将 dna1 的前 i 个字符转换为 dna2 的前 j 个字符所需的最少编辑步骤。

状态转移方程:
  1. 初始条件

    • dp[0][j] = j:将空字符串转换为 dna2 的前 j 个字符需要 j 次插入操作。
    • dp[i][0] = i:将 dna1 的前 i 个字符转换为空字符串需要 i 次删除操作。
  2. 状态转移

    • 如果 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;
}

代码解释:

  1. 初始化动态规划表:创建一个 (m+1) x (n+1) 的二维数组 dp,其中 m 和 n 分别是 dna1 和 dna2 的长度。
  2. 初始化边界条件:设置 dp[i][0] = i 和 dp[0][j] = j,分别表示将 dna1 或 dna2 转换为空字符串所需的编辑步骤。
  3. 填充动态规划表:使用双重循环遍历 dna1 和 dna2 的所有字符,根据状态转移方程更新 dp 表。
  4. 返回最终结果dp[m][n] 即为将 dna1 转换为 dna2 所需的最少编辑步骤。