题目解析
题目
小R正在研究DNA序列,他需要一个函数来计算将一个受损DNA序列(dna1)转换成一个未受损序列(dna2)所需的最少编辑步骤。编辑步骤包括:增加一个碱基、删除一个碱基或替换一个碱基。
解析
这是一个典型的动态规划问题 将dna1转换为dna2有三种方法 1.增加一个碱基 2.删除一个碱基 3.替换一个碱基 我们采用二维数组dp[i][j]来表示将dna1的前i个字符转换为dna2的前j个字符所需要的编辑次数
js
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个字符所需要的次数
js
if dna1[i - 1] == dna2[j - 1]: # 字符相同,不需要操作
dp[i][j] = dp[i - 1][j - 1]
当dna1的前i个字符和dna2的前j个字符不同时,需要进行编辑
对于三种编辑方法来说
增加一个碱基
当dna1的前i个字符和dna2的前j-1个字符相同时,采用增加一个碱基的方法会使编辑次数最少 此时有:
js
dp[i][j] = dp[i][j-1]+1
即: 将dna1的前i个字符编辑为dna2的前j个字符所需要的次数等于将dna1的前i个字符编辑为dna2的前j-1个字符所需要的次数+1
删除一个碱基
当dna1的前i-1个字符和dna2的前j个字符相同时,采用删除一个碱基的方法会使编辑次数最少 此时有:
js
dp[i][j] = dp[i-1][j]+1
即: 将dna1的前i个字符编辑为dna2的前j个字符所需要的次数等于将dna1的前i-1个字符编辑为dna2的前j个字符所需要的次数+1
替换一个碱基
当dna1的前i-1个字符和dna2的前j-1个字符相同时,采用替换一个碱基的方法会使编辑次数最少 此时有:
js
dp[i][j] = dp[i-1][j-1]+1
即: 将当dna1的前i个字符编辑为dna2的前j个字符所需要的次数等于将当dna1的前i-1个字符编辑为dna2的前j-1个字符所需要的次数+1
dp初始化
对于dp数组来说,
js
dp[0][0] = 0
即将dna1的前0个字符编辑为dna2的前0个字符所需要的次数等于0
js
dp[i][0] = i
即将dna1的前i个字符编辑为dna2的前0个字符所需要的次数等于i(删除dna1的i个字符)
js
dp[0][j] = j
即将dna1的前0个字符编辑为dna2的前j个字符所需要的次数等于i(向dna1增加dna2的j个字符)
填充dp表
从dp数组的开头开始进行最小编辑次数的计算,将dp赋值为三种编辑方法中最小的数值
js
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所需的最小编辑次数
代码
js
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)