第81题解析 古生物DNA序列血缘分析 | 豆包MarsCode AI刷题

49 阅读3分钟

首先总结一下动态规划问题

动态规划(Dynamic Programming,简称 DP)是一种将复杂问题分解为简单子问题的算法思想,广泛应用于解决最优化问题。通过将每个子问题的结果保存起来,避免了重复计算,从而提高了效率。以下是动态规划的核心思想和常见问题类型的总结:

1、动态规划的核心思想

问题分解:将问题分解为多个相似的子问题,并通过递归求解这些子问题。

重叠子问题:子问题之间有重叠,意味着多个子问题会计算相同的值,利用存储这些子问题的解来避免重复计算。

最优子结构:问题的最优解可以通过子问题的最优解来推导。例如,最短路径问题的最优解可以通过中间点的最短路径来推导。

状态转移方程:通过定义状态和状态之间的转换关系来递推求解。

  1. 动态规划的解题步骤

定义状态:找出问题的“状态”,即问题在某一阶段的关键变量,通常是数组或矩阵。

确定状态转移方程:找出从一个状态到另一个状态的转移规则。

初始条件:确定最基本的情况,即递归的终止条件。

计算顺序:通常是从小到大(或从大到小),避免重复计算,逐步推导出最终解。

 

要解决这个问题,我们可以使用动态规划(Dynamic Programming)来计算两条DNA序列之间的最小变异次数。以下是解题思路:

问题理解

我们需要计算两条DNA序列之间的最小变异次数,变异方式包括:

  1. 插入一个核苷酸

  2. 删除一个核苷酸

  3. 替换一个核苷酸

数据结构选择

我们可以使用一个二维数组 dp,其中 dp[i][j] 表示 dna1 的前 i 个字符和 dna2 的前 j 个字符之间的最小变异次数。

算法步骤

  1. 初始化

   - 如果 dna1 为空,那么变异次数就是 dna2 的长度(全部插入)。

   - 如果 dna2 为空,那么变异次数就是 dna1 的长度(全部删除)。

  1. 状态转移

   - 如果 dna1[i-1] == dna2[j-1],那么 dp[i][j] = dp[i-1][j-1],因为不需要变异。

   - 否则,dp[i][j] 可以通过以下三种方式得到:

     - 从 dp[i-1][j] 删除一个字符(dna1 的第 i 个字符)。

     - 从 dp[i][j-1] 插入一个字符(dna2 的第 j 个字符)。

     - 从 dp[i-1][j-1] 替换一个字符(dna1 的第 i 个字符替换为 dna2 的第 j 个字符)。

   - 取这三种方式的最小值,并加上1(表示一次变异)。

 

  1. 最终结果

   - dp[len(dna1)][len(dna2)] 就是 dna1dna2 之间的最小变异次数。

代码实现如下

#编写函数
def solution(dna1, dna2):
    m, n = len(dna1), len(dna2)
    # 创建一个 (m+1) x (n+1) 的二维数组 dp,dp[i][j] 表示将 dna1[:i] 转换为 dna2[:j] 的最小变异次数
    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 次插入

    # 填充 dp 表
    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,    # 删除 dna1[i-1]
                    dp[i][j - 1] + 1,    # 插入 dna2[j-1]
                    dp[i - 1][j - 1] + 1 # 替换 dna1[i-1] 为 dna2[j-1]
                )

    # 返回 dp[m][n],即将 dna1 转换为 dna2 的最小变异次数
    return dp[m][n]



if __name__ == "__main__":
    #  You can add more test cases here
    print(solution("AGT", "AGCT") == 1)
    print(solution("", "ACGT") == 4)
    print(solution("GCTAGCAT", "ACGT") == 5)