青训营X豆包MarsCode 技术训练营第三次 | 豆包MarsCode AI 刷题

67 阅读4分钟

DNA序列编辑距离

题目描述:小R需要一个函数来计算将一个受损的DNA(dna1)转换成一个未受损序列(dna2)所需要的最少编辑步骤。编辑步骤包括:增加一个碱基,删除一个碱基或者替换一个碱基。 解题思路:需要计算将一个DNA序列 dna1 转换成另一个DNA序列 dna2 所需的最少编辑步骤。编辑步骤包括: 插入:在 dna1 中插入一个碱基。 删除:从 dna1 中删除一个碱基。 替换:将 dna1 中的一个碱基替换为另一个碱基。

可以使用动态规划算法来解决这个问题:具体来说是Levenshtein距离算法的一种变体,专门用于DNA序列。

(一)动态规划矩阵初始化

1创建dp矩阵
  • 在代码中,首先创建了一个二维列表dp,使用dp = [[0] * (len(dna2)+1) for _ in range(len(dna1)+1)]语句。这就像是我在为自己准备一个记录操作步骤的表格,表格的行数是len(dna1)+1,列数是len(dna2)+1。这个表格中的每个元素dp[i][j]将存储把dna1的前i个字符转换为dna2的前j个字符所需的最少编辑步骤。
- 2. 初始化第一列

接着,有一个for i in range(len(dna1)+1)循环,在这个循环中,dp[i][0]=i。从我的角度来看,我在考虑一种特殊情况,就是将dna1的前i个字符转换为空字符串。因为要达到这个目的,只能逐个删除dna1中的字符,所以需要i次删除操作,这就是为什么要将dp[i][0]设置为i

3初始化第一行
  • 类似地,for j in range(len(dna2)+1)循环将dp[0][j]=j。这是在考虑另一种特殊情况,即将空字符串转换为dna2的前j个字符。要做到这一点,只能逐个插入dna2中的字符,所以需要j次插入操作,因此将dp[0][j]设置为j.

- ### 二)动态规划计算

  1. 嵌套循环填充dp矩阵

    • 然后,有两个嵌套的for循环for i in range(1, len(dna1)+1)for j in range(1, len(dna2)+1)。这就像是我开始逐行逐列地填写之前创建的那个记录操作步骤的表格。
    • 对于每一个ij的组合,我都要确定dp[i][j]的值,也就是把dna1的前i个字符转换为dna2的前j个字符的最少编辑步骤。
  2. 字符相同情况

    • dna1[i - 1]==dna2[j - 1]时(这里要注意,因为dp矩阵的索引从0开始,而dna1dna2的字符索引也是从0开始,所以实际比较的是dna1中的第i个字符和dna2中的第j个字符),我发现不需要进行任何编辑操作。此时,dp[i][j]=dp[i - 1][j - 1]。这就好比我在填写表格时,如果发现当前位置对应的dna1dna2的字符相同,那么将dna1的前i个字符转换为dna2的前j个字符的最少编辑步骤就和去掉这两个相同字符后的子序列的最少编辑步骤是一样的。
  3. 字符不同情况

    • dna1[i - 1]!=dna2[j - 1]时,我需要考虑三种编辑操作(删除、插入和替换)中的最优操作。此时,dp[i][j]=min(dp[i - 1][j]+1, dp[i][j - 1]+1, dp[i - 1][j - 1]+1)

    (三)返回结果

最终结果
  • 整个dp矩阵的填充后,右下角的元素dp[len(dna1)][len(dna2)]就代表了将整个dna1转换为整个dna2所需的最少编辑步骤,所以我将这个值作为函数的结果返回。
  • 代码部分`:
````###### def solution(dna1, dna2):
###### dp = [[0] * (len(dna2) + 1) for _ in range(len(dna1) + 1)]
###### 
###### for i in range(len(dna1) + 1):
###### dp[i][0] = i  # 将dna1的前i个字符转换成空字符串需要i次删除操作
###### for j in range(len(dna2) + 1):
###### dp[0][j] = j  # 将空字符串转换成dna2的前j个字符需要j次插入操作
###### 
###### 填充dp数组
###### 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 - 1][j] + 1,    # 删除
###### dp[i][j - 1] + 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)

**总结:**本次题目有一定难度,需要理清顺序,认真分析题目,搞清楚代表的含义,运用动态规划分析即可完成题目。