DNA编辑分析 | 豆包MarsCode AI 刷题

50 阅读3分钟

问题描述

小R正在研究DNA序列,他需要一个函数来计算将一个受损DNA序列(dna1)转换成一个未受损序列(dna2)所需的最少编辑步骤。编辑步骤包括:增加一个碱基、删除一个碱基或替换一个碱基。

问题解析

和力扣的编辑距离是一样的,采用多维动态规划来解决。

动态规划:

动态规划的核心思想是将问题分解为更小的子问题,并利用已经解决的子问题的解来构建更大问题的解。

那么我们可以定义dp[i][j]来表示将dna1的前i个字符转为dna2的前j的字符所需的最小编辑步骤。

  1. i->0:需要i次删除
dp[i][0] = i
  1. 0->j:需要j次添加
dp[0][j] = j
  1. 都不为0的情况下,则根据dna1[i - 1] 和 dna2[j - 1]的值判断dp[i][j]是否相对于dp[i-1][j-1]有变化。
for i:=1;i<=m;i++{
    for j:=1;j<=n;j++{
        if word1[i-1]==word2[j-1]{
            dp[i][j]=dp[i-1][j-1]
        }else{
            dp[i][j]=min(dp[i-1][j],min(dp[i][j-1],dp[i-1][j-1]))+1
        }
    }
}

我们来画一个图理解一下:

这是做完第一步和第二步的样子

编辑距离初始化.png

我们一步一步来把dp填满

  1. dp[1][1]就是dna1的前一个字符串到dna2的前一个字符串的所需转换步骤,我们比较一下dna1[0]dna2[0],因为A==A所以不需要转换,dp[1][1]=0(需要注意数组的下标和切片下标的区别,这就是为什么要比较dna1[i - 1] 和 dna2[j - 1]而不是dna1[i] 和 dna2[j]的原因)
  2. dp[1][2]就是dna1的前一个字符串到dna2的前两个字符串的所需转换步骤,我们比较一下dna1[0]dna2[1],因为A!=G所以需要转换,因为要找到最小的转换次数就需要保证每一步都是最小的所以取min+1。而dp[i-1][j] + 1表示从dna1的前i-1个字符转换为dna2的前j个字符的最小步骤数,意味着删除dna1的第i个字符,从而使得前i-1个字符与dna2的前j个字符匹配;dp[i][j-1] + 1:表示从dna1的前i个字符转换为dna2的前j-1个字符的最小步骤数,加一意味着在dna1的末尾插入一个与dna2的第j个字符相同的字符以便匹配;dp[i-1][j-1] + 1:表示从dna1的前i-1个字符转换为dna2的前j-1个字符的最小步骤数,则加一是指需要将dna1的第i个字符替换为dna2的第j个字符
  3. ......

下图是最后的填充结果: Pasted image 20241105194623.png

完整的go代码:

m,n:=len(dna1),len(dna2)
dp:=make([][]int,m+1
for i:=range dp{
	dp[i]=make([]int,n+1)
}
for i:=0;i<=m;i++{
	dp[i][0]=i
}
for j:=0;j<=n;j++{
	dp[0][j]=j
}
for i:=1;i<=m;i++{
    for 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],min(dp[i][j-1],dp[i-1][j-1]))+1
        }
    }
}
return dp[m][n]

完整python代码:

def solution(dna1, dna2):
    m = len(dna1)
    n = len(dna2)
    dp = [[0] * (n + 1) for _ in range(m + 1)]

    for i in range(m + 1):

        dp[i][0] = i  # 从 dna1 的前 i 个字符到空串需要 i 步删除

    for j in range(n + 1):

        dp[0][j] = j  # 从空串到 dna2 的前 j 个字符需要 j 步插入

    # 填充矩阵

    for i in range(1, m + 1):

        for j in range(1, n + 1):

            if dna1[i - 1] == dna2[j - 1]:

                dp[i][j] = dp[i - 1][j - 1]  

            else:

                dp[i][j] = min(

                    dp[i - 1][j] + 1,    

                    dp[i][j - 1] + 1,   

                    dp[i - 1][j - 1] + 1 

                )

    return dp[m][n]