第二轮66刷题任务over!放图待补充
DNA序列编辑问题
问题描述
问题分析
这是一道经典的 编辑距离问题,常用于比较字符串的相似性,DNA序列比对是其中的典型应用。
这个问题的核心是计算两个字符串之间的最小变换成本,DNA序列问题本质是一个字符串编辑问题,其实质是找到 dna1 和 dna2 的 最大相似子序列,通过最小化编辑操作来实现转换:
- 插入操作: 模拟 DNA 链中插入新的碱基对;
- 删除操作: 模拟 DNA 链中删除已有碱基对;
- 替换操作: 模拟突变,即将一个碱基对变成另一个碱基对
动态规划状态分析:
-
状态定义:
- 用
dp[i][j]表示将dna1的前i个字符转换为dna2的前j个字符所需的最小操作数。
- 用
-
状态转移:
-
假设当前考虑的是
dna1的第i个字符和dna2的第j个字符:-
如果两字符相同,即
dna1[i-1] == dna2[j-1],不需要额外操作: dp[i][j]=dp[i−1][j−1]dp[i][j] = dp[i-1][j-1]dp[i][j]=dp[i−1][j−1] -
如果两字符不同,需要考虑三种可能的操作:
- 删除字符
dna1[i-1](相当于从i-1位置转移到i位置):dp[i-1][j] + 1; - 插入字符
dna2[j-1](相当于从j-1位置转移到j位置):dp[i][j-1] + 1; - 替换字符
dna1[i-1]为dna2[j-1](相当于直接替换):dp[i-1][j-1] + 1。
- 删除字符
-
状态转移方程: dp[i][j]=min(dp[i−1][j]+1,dp[i][j−1]+1,dp[i−1][j−1]+1)dp[i][j] = \min(dp[i-1][j] + 1, dp[i][j-1] + 1, dp[i-1][j-1] + 1)dp[i][j]=min(dp[i−1][j]+1,dp[i][j−1]+1,dp[i−1][j−1]+1)
-
-
-
初始化:
- 如果
dna1是空串(长度为0),需要将dna2的所有字符插入: dp[0][j]=jdp[0][j] = jdp[0][j]=j - 如果
dna2是空串(长度为0),需要删除dna1的所有字符: dp[i][0]=idp[i][0] = idp[i][0]=i
- 如果
-
结果:
- 我们需要的最小编辑距离就是
dp[m][n],其中m和n分别是dna1和dna2的长度。
- 我们需要的最小编辑距离就是
Python代码
def solution(dna1, dna2):
m = len(dna1)
n = len(dna2)
# 创建一个(m+1) x (n+1)的二维列表来保存编辑距离
dp = [[0] * (n + 1) for _ in range(m + 1)]
# 初始化第一行和第一列
for i in range(m + 1):
dp[i][0] = i # 从dna1到空字符串的操作数为i(删除操作)
for j in range(n + 1):
dp[0][j] = j # 从空字符串到dna2的操作数为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, # 删除操作
dp[i][j - 1] + 1, # 插入操作
dp[i - 1][j - 1] + 1) # 替换操作
return dp[m][n] # 返回最终的编辑距离
if __name__ == "__main__":
# You can add more test cases here
print(solution("AGCTTAGC", "AGCTAGCT") == 2 )
print(solution("AGCCGAGC", "GCTAGCT") == 4)