AI刷题-动态规划“DNA序列编辑距离” | 豆包MarsCode AI刷题

38 阅读4分钟

题目解析

题目

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

解析

这是一个典型的动态规划问题 将dna1转换为dna2有三种方法 1.增加一个碱基 2.删除一个碱基 3.替换一个碱基 我们采用二维数组dp[i][j]来表示将dna1的前i个字符转换为dna2的前j个字符所需要的编辑次数

    dp = [[0] * (len2 + 1) for _ in range(len1 + 1)]

具体解法

当dna1的前i个字符和dna2的前j个字符相同时,不需要进行编辑 此时有: dp[i][j] = dp[i-1][j-1] 即: 将dna1的前i个字符编辑为dna2的前j个字符所需要的次数等于将dna1的前i-1个字符编辑为dna2的前j-1个字符所需要的次数

if dna1[i - 1] == dna2[j - 1]:  # 字符相同,不需要操作
                dp[i][j] = dp[i - 1][j - 1]

当dna1的前i个字符和dna2的前j个字符不同时,需要进行编辑

对于三种编辑方法来说

增加一个碱基

当dna1的前i个字符和dna2的前j-1个字符相同时,采用增加一个碱基的方法会使编辑次数最少 此时有:

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

即: 将dna1的前i个字符编辑为dna2的前j个字符所需要的次数等于将dna1的前i个字符编辑为dna2的前j-1个字符所需要的次数+1

删除一个碱基

当dna1的前i-1个字符和dna2的前j个字符相同时,采用删除一个碱基的方法会使编辑次数最少 此时有:

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

即: 将dna1的前i个字符编辑为dna2的前j个字符所需要的次数等于将dna1的前i-1个字符编辑为dna2的前j个字符所需要的次数+1

替换一个碱基

当dna1的前i-1个字符和dna2的前j-1个字符相同时,采用替换一个碱基的方法会使编辑次数最少 此时有:

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

即: 将当dna1的前i个字符编辑为dna2的前j个字符所需要的次数等于将当dna1的前i-1个字符编辑为dna2的前j-1个字符所需要的次数+1

dp初始化

对于dp数组来说,

dp[0][0] = 0

即将dna1的前0个字符编辑为dna2的前0个字符所需要的次数等于0

dp[i][0] = i 

即将dna1的前i个字符编辑为dna2的前0个字符所需要的次数等于i(删除dna1的i个字符)

 dp[0][j] = j

即将dna1的前0个字符编辑为dna2的前j个字符所需要的次数等于i(向dna1增加dna2的j个字符)

填充dp表

从dp数组的开头开始进行最小编辑次数的计算,将dp赋值为三种编辑方法中最小的数值

    for i in range(1, len1 + 1):
        for j in range(1, len2 + 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)  # 替换

最终返回dp[len1][len2]的值,即为将dna1转换为dna2所需的最小编辑次数

代码

def solution(dna1, dna2):
    # 获取两个DNA序列的长度
    len1, len2 = len(dna1), len(dna2)
    
    # 创建一个二维数组来存储编辑距离
    dp = [[0] * (len2 + 1) for _ in range(len1 + 1)]
    
    # 初始化边界条件
    for i in range(len1 + 1):
        dp[i][0] = i  # 删除所有字符
    for j in range(len2 + 1):
        dp[0][j] = j  # 添加所有字符
    
    # 填充dp表
    for i in range(1, len1 + 1):
        for j in range(1, len2 + 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[len1][len2]

if __name__ == "__main__":
    print(solution("AGT", "AGCT") == 1)
    print(solution("AACCGGTT", "AACCTTGG") == 4)
    print(solution("ACGT", "TGC") == 3)
    print(solution("A", "T") == 1)
    print(solution("GGGG", "TTTT") == 4)

参考内容

[轻松掌握动态规划]6.编辑距离_哔哩哔哩_bilibili