81.古生物DNA血缘分析<字节青训营-中等题>

108 阅读4分钟

1.问题

问题描述

小U是一位古生物学家,正在研究不同物种之间的血缘关系。为了分析两种古生物的血缘远近,她需要比较它们的DNA序列。DNA由四种核苷酸A、C、G、T组成,并且可能通过三种方式发生变异:添加一个核苷酸、删除一个核苷酸或替换一个核苷酸。小U认为两条DNA序列之间的最小变异次数可以反映它们之间的血缘关系:变异次数越少,血缘关系越近。

你的任务是编写一个算法,帮助小U计算两条DNA序列之间所需的最小变异次数。

  • dna1: 第一条DNA序列。
  • dna2: 第二条DNA序列。

测试样例

样例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

2.思路

这道题和之前的一道题比较相似:2024字节青训营刷题日记入营考核:【✍️ 入营考核】AI 加码,青训营 X 豆包MarsCode 技术训练营👏 欢迎 - 掘金中的第四题: DNA序列距离编辑(16) 使用动态规划的思路。

定义一个二维数组 dp,其中 dp[i][j] 表示将 dna1 的前 i 个字符转换为 dna2 的前 j 个字符所需的最小操作数

  • 初始状态: dp[0][0] = 0,因为将空字符串转换为空字符串不需要任何操作。 dp[i][0] = i,因为将前 i 个字符的 dna1 转换为空字符串需要 i 次删除操作。 dp[0][j] = j,因为将空字符串转换为前 j 个字符的 dna2 需要 j 次插入操作。

  • 状态转移方程: 如果 dna1[i-1] == dna2[j-1](说明第i个和第j个字符相同),则 dp[i][j] = dp[i-1][j-1],因为最后一个字符相同,不需要额外操作。 如果dna1[i-1] != dna2[j-1] 则需要考虑三种操作:

    • 插入操作:dp[i][j] = dp[i][j-1] + 1
    • 删除操作:dp[i][j] = dp[i-1][j] + 1
    • 替换操作:dp[i][j] = dp[i-1][j-1] + 1

注意:

  1. df初始化时,行数为[0,len(dna1)+1),说明从前0个字符到前len(len(dna1))均可向dna2转化
  2. 如果dna1[i-1] != dna2[j-1],取删除、插入、替换下步数最小的
  3. dna中的i-1,j-1对应dp中的i,j

3.代码

我的代码

#include <iostream>
#include <string>
using namespace std;

int solution(std::string dna1, std::string dna2) {
    // Please write your code here
    int m = dna1.size();
    int n = dna2.size();
    int dp[m + 1][n + 1];
    dp[0][0] = 0;
    dp[0][n] = n;
    dp[m][0] = m;
    for(int i = 1; i <= m; i++){
        for(int j = 1; j <= n; j++){
            if (dna1[i - 1] == dna2 [j - 1]){
                dp[i + 1][j + 1] = dp[i][j];
            } 
            else {
            // min函数只接受两个参数
            dp[i][j] = std::min(std::min(dp[i - 1][j] + 1, dp[i][j - 1] + 1), dp[i - 1][j - 1] + 1);
            }
        }
    }
    return dp[m][n];
}

int main() {
    // You can add more test cases here
    std::cout << (solution("AGT", "AGCT") == 1) << std::endl;
    std::cout << (solution("", "ACGT") == 4) << std::endl;
    std::cout << (solution("GCTAGCAT", "ACGT") == 5) << std::endl;
    return 0;
}
  • 初始化数组问题: 你在初始化 dp 数组时使用了 dp[0][0] = 0; dp[0][n] = n; dp[m][0] = m;,但这会导致数组越界,应该分别初始化 dp[0][j]dp[i][0]
  • 数组越界问题: 在动态规划更新 dp 数组时,你使用了 dp[i + 1][j + 1],这会导致越界错误。应该使用 dp[i][j] 而不是 dp[i + 1][j + 1]
#include <iostream>
#include <string>
using namespace std;

int solution(std::string dna1, std::string dna2) {
    // Please write your code here
    int m = dna1.size();
    int n = dna2.size();
    int dp[m + 1][n + 1];
    dp[0][0] = 0;
    for (int i = 1; i <= m; i++){
        dp[i][0] = i;
    }
    for (int j = 1; 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 {
            // min函数只接受两个参数
            dp[i][j] = std::min(std::min(dp[i - 1][j] + 1, dp[i][j - 1] + 1), dp[i - 1][j - 1] + 1);
            }
        }
    }
    return dp[m][n];
}

int main() {
    // You can add more test cases here
    std::cout << (solution("AGT", "AGCT") == 1) << std::endl;
    std::cout << (solution("", "ACGT") == 4) << std::endl;
    std::cout << (solution("GCTAGCAT", "ACGT") == 5) << std::endl;
    return 0;
}

参考代码

def solution(dna1, dna2):
    # Please write your code here
    # 状态
    # dna1长度为行数,dna2长度为列数
    dp = [[0 for _ in range(len(dna2) + 1)] for _ in range(len(dna1) + 1)]
    # 初始条件
    dp[0][0] = 0
    # dp[i][0] = i0列等于行号
    for i in range(1, len(dna1) + 1):
        dp[i][0] = i
    # dp[0][j] = j0行等于列号
    for j in range(1, len(dna2) + 1):
        dp[0][j] = j
    for i in range(1, len(dna1) + 1):
        for j in range(1, len(dna2) + 1):
            if dna1[i-1] == dna2[j-1]:
                dp[i][j] = dp[i-1][j-1]
            else:
                dp[i][j] = min(dp[i][j-1] + 1, dp[i-1][j] + 1, dp[i-1][j-1] + 1)
    return dp[len(dna1)][len(dna2)]

if __name__ == "__main__":
    #  You can add more test cases here
    print(solution("AGCTTAGC", "AGCTAGCT") == 2 )
    print(solution("AGCCGAGC", "GCTAGCT") == 4)