古生物DNA序列血缘分析| 豆包MarsCode AI刷题

98 阅读3分钟

题目要求:

小U是一位古生物学家,正在研究不同物种之间的血缘关系。为了分析两种古生物的血缘远近,她需要比较它们的DNA序列。DNA由四种核苷酸A、C、G、T组成,并且可能通过三种方式发生变异:添加一个核苷酸、删除一个核苷酸或替换一个核苷酸。小U认为两条DNA序列之间的最小变异次数可以反映它们之间的血缘关系:变异次数越少,血缘关系越近。

你的任务是编写一个算法,帮助小U计算两条DNA序列之间所需的最小变异次数。

dna1: 第一条DNA序列。 dna2: 第二条DNA序列。

测试样例:

样例1:

输入:dna1 = "AGT",dna2 = "AGCT" 输出:1

样例2:

输入:dna1 = "AACCGGTT",dna2 = "AACCTTGG" 输出:4

样例3:

输入:dna1 = "ACGT",dna2 = "TGC" 输出:3

样例4:

输入:dna1 = "A",dna2 = "T" 输出:1

样例5:

输入:dna1 = "GGGG",dna2 = "TTTT" 输出:4

题目难度:

中等

解题语言:

python

解题思路:

这个问题可以通过动态规划来解决,因为我们需要计算两条DNA序列之间的最小变异次数。我们可以使用一个二维数组dp来存储中间结果,其中dp[i][j]表示dna1的前i个字符和dna2的前j个字符之间的最小变异次数。

那么如何用上一段中提到的动态规划来解决这个问题?

其关键点在于首先要初始化:设置边界条件,处理空序列的情况:当dna1为空时,dp[0][j]表示将空序列变为dna2的前j个字符所需的最小变异次数,显然是j次插入操作。 当dna2为空时,dp[i][0]表示将dna1的前i个字符变为空序列所需的最小变异次数,显然是i次删除操作。

其次进行状态转移:根据当前字符是否相等,选择最小的变异操作:如果dna1[i-1] == dna2[j-1],则dp[i][j] = dp[i-1][j-1],因为不需要变异。 否则,dp[i][j]可以通过以下三种操作的最小值得到:

最后得出结果:返回最终的dp数组中的值,即dp[len(dna1)][len(dna2)]。

方法步骤:

1.初始化:创建一个二维数组dp,大小为(len(dna1) + 1) x (len(dna2) + 1)。

2.边界条件:dp[0][j]表示dna1为空时,需要j次插入操作将dna1变为dna2的前j个字符。

dp[i][0]表示dna2为空时,需要i次删除操作将dna1的前i个字符变为dna2。

3.状态转移: 如果dna1[i-1] == dna2[j-1],则dp[i][j] = dp[i-1][j-1],因为不需要变异。

否则,dp[i][j]可以通过以下三种操作的最小值得到:

插入:dp[i][j-1] + 1

删除:dp[i-1][j] + 1

替换:dp[i-1][j-1] + 1

解答代码:

def solution(dna1, dna2):
# 初始化dp数组
dp = [[0] * (len(dna2) + 1) for _ in range(len(dna1) + 1)]

# 边界条件
for i in range(1, len(dna1) + 1):
    dp[i][0] = i
for j in range(1, len(dna2) + 1):
    dp[0][j] = j

# 状态转移
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], dp[i][j-1], dp[i-1][j-1]) + 1

# 返回结果
return dp[len(dna1)][len(dna2)]

if __name__ == "__main__":
# 你可以添加更多测试用例
print(solution("AGT", "AGCT") == 1)
print(solution("", "ACGT") == 4)
print(solution("GCTAGCAT", "ACGT") == 5)