题目解析
题目描述
小R正在研究DNA序列,他需要一个函数来计算将一个受损的DNA序列(dna1)转换为一个未受损的序列(dna2)所需的最少编辑步骤。编辑步骤包括:
- 增加一个碱基
- 删除一个碱基
- 替换一个碱基
解析
这是一个典型的动态规划(DP)问题。我们需要通过三种编辑方法来将dna1转化为dna2,即:
- 增加一个碱基
- 删除一个碱基
- 替换一个碱基
为了计算编辑步骤的最小值,我们可以使用一个二维数组 dp[i][j],表示将dna1的前i个字符转换为dna2的前j个字符所需的编辑步骤。
动态规划解法
1. 初始化DP数组
首先,我们需要初始化一个 dp 数组,其中 dp[i][j] 表示将 dna1 的前 i 个字符编辑为 dna2 的前 j 个字符所需的最小操作次数。
-
边界条件:
- 如果
dna1的前i个字符转换为一个空字符串"",那么所需的操作次数是i次(删除操作)。 - 如果
dna2的前j个字符转换为空字符串"",那么所需的操作次数是j次(插入操作)。
- 如果
2. 状态转移
-
当字符相同:如果
dna1[i-1] == dna2[j-1],不需要进行任何操作,那么:python 複製程式碼 dp[i][j] = dp[i-1][j-1] -
当字符不同:我们需要考虑三种操作:
-
增加一个碱基:如果我们将
dna1的前i个字符编辑为dna2的前j-1个字符,然后在末尾加一个碱基(即dna2[j-1]),此时的操作次数为:python 複製程式碼 dp[i][j] = dp[i][j-1] + 1 -
删除一个碱基:如果我们将
dna1的前i-1个字符编辑为dna2的前j个字符,然后删除dna1[i-1],此时的操作次数为:python 複製程式碼 dp[i][j] = dp[i-1][j] + 1 -
替换一个碱基:如果我们将
dna1的前i-1个字符编辑为dna2的前j-1个字符,然后替换dna1[i-1]为dna2[j-1],此时的操作次数为:python 複製程式碼 dp[i][j] = dp[i-1][j-1] + 1
-
因此,最终的状态转移公式为:
python
複製程式碼
dp[i][j] = min(dp[i-1][j] + 1, # 删除
dp[i][j-1] + 1, # 插入
dp[i-1][j-1] + 1) # 替换
3. 最终结果
最终返回 dp[len1][len2],即将 dna1 转换为 dna2 所需的最小编辑步骤。
代码实现
python
複製程式碼
def solution(dna1, dna2):
len1, len2 = len(dna1), len(dna2)
# 创建DP数组,并初始化
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)
代码解读
-
初始化:
dp[i][0]表示将dna1的前i个字符转为空字符串所需的删除次数,初始化为i。dp[0][j]表示将空字符串转为dna2的前j个字符所需的插入次数,初始化为j。
-
填充 DP 表:
- 对于每一对
i和j,如果dna1[i-1]与dna2[j-1]相同,不需要编辑,直接继承上一个状态:dp[i][j] = dp[i-1][j-1]。 - 如果不相同,则选择删除、插入或替换中的最小值作为最优编辑步骤。
- 对于每一对
-
返回结果:
- 最终的编辑步骤就是
dp[len1][len2],即将dna1转换为dna2所需的最小编辑次数。
- 最终的编辑步骤就是
测试用例
python
複製程式碼
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
小结
通过动态规划,我们能够高效地解决将一个DNA序列转换为另一个DNA序列所需的最少编辑步骤问题。通过构建一个二维数组来存储中间状态,并根据三种基本操作(插入、删除、替换)进行状态转移,最终得出最小编辑步骤。