【2024字节青训·易】DNA序列还原
问题描述
给定一段受损的 DNA 碱基序列 dna1,在每次只操作一个碱基的情况下,将其以最少的操作步骤将其还原到未受损的 DNA 碱基序列 dna2。
只可以对 DNA 碱基序列中的一个碱基进行三种操作:
- 增加一个碱基
- 去除一个碱基
- 替换一个碱基
输入描述:
输入两段 DNA 碱基序列,每段分一行输入
第一行为第一段受损的 DNA 碱基序列 dna1
第二行为第二段未受损的 DNA 碱基序列 dna2
输出描述:
最小操作步骤数
备注:
0 <= dna1.length, dna2.length <= 500
dna1 和 dna2 由大写英文字母 A、G、C、T 组成
示例 1
输入
AGCTTAGC
AGCTAGCT
输出
2
说明
AGCTTAGC -> AGCTAGC(删除 T)
AGCTAGC -> AGCTAGCT(增加 T)
示例 2
输入
AGCCGAGC
GCTAGCT
输出
4
说明
AGCCGAGC -> GCCGAGC(删除 A)
GCCGAGC -> GCTGAGC(将 C 替换为 T)
GCTGAGC -> GCTAGC(删除 G)
GCTAGC -> GCTAGCT(增加 T)
要解决这个问题,我们可以使用动态规划(Dynamic Programming, DP)的方法。这个方法非常适合解决涉及字符串编辑距离的问题,比如 DNA 碱基序列的转换。我们将使用经典的编辑距离算法(也称为莱文斯坦距离算法)。
动态规划方法
我们定义一个二维数组 dp,其中 dp[i][j] 表示将 dna1 的前 i 个字符转换为 dna2 的前 j 个字符所需的最小操作数。
状态转移方程
-
初始状态:
dp[0][0] = 0,因为将空字符串转换为空字符串不需要任何操作。dp[i][0] = i,因为将前i个字符的dna1转换为空字符串需要i次删除操作。dp[0][j] = j,因为将空字符串转换为前j个字符的dna2需要j次插入操作。
-
状态转移方程:
-
如果
dna1[i-1] == dna2[j-1],则dp[i][j] = dp[i-1][j-1],因为最后一个字符相同,不需要额外操作。 -
如果
dna1[i-1] != dna2[j-1],则需要考虑三种操作:
- 插入操作:
dp[i][j] = dp[i][j-1] + 1 - 删除操作:
dp[i][j] = dp[i-1][j] + 1 - 替换操作:
dp[i][j] = dp[i-1][j-1] + 1
- 插入操作:
-
综合起来就是:
-
d p [ i ] [ j ] = min ( d p [ i − 1 ] [ j ] + 1 , d p [ i ] [ j − 1 ] + 1 , d p [ i − 1 ] [ j − 1 ] + ( d n a 1 [ i − 1 ] ≠ d n a 2 [ j − 1 ] ) ) dp[i][j] = \min(dp[i-1][j] + 1, dp[i][j-1] + 1, dp[i-1][j-1] + (dna1[i-1] \neq dna2[j-1])) dp[i][j]=min(dp[i−1][j]+1,dp[i][j−1]+1,dp[i−1][j−1]+(dna1[i−1]=dna2[j−1]))
-
def solution(dna1, dna2):
m, n = len(dna1), len(dna2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(1, m+1):
dp[i][0] = i
for j in range(1, n+1):
dp[0][j] = j
dp[0][0] = 0
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)
# Please write your code here
return dp[m][n]
if __name__ == "__main__":
# You can add more test cases here
print(solution("AGCTTAGC", "AGCTAGCT") == 2 )
print(solution("AGCCGAGC", "GCTAGCT") == 4)

4363

被折叠的 条评论
为什么被折叠?



