def solution(dna1, dna2): m, n = len(dna1), len(dna2) # 创建一个 (m+1) x (n+1) 的二维数组 dp dp = [[0] * (n + 1) for _ in range(m + 1)] # 初始化 dp 数组 for i in range(m + 1): dp[i][0] = i for j in range(n + 1): dp[0][j] = j # 填充 dp 数组 for i in range(1, m + 1): for j in range(1, n + 1): # 计算插入、删除和替换的代价 insert_cost = dp[i][j-1] + 1 delete_cost = dp[i-1][j] + 1 replace_cost = dp[i-1][j-1] + (1 if dna1[i-1] != dna2[j-1] else 0) # 取最小值 dp[i][j] = min(insert_cost, delete_cost, replace_cost) # 返回最终结果 return dp[m][n] if name == "main": # You can add more test cases here print(solution("AGT", "AGCT") == 1) print(solution("", "ACGT") == 4) print(solution("GCTAGCAT", "ACGT") == 5) 这段代码实现了编辑距离(Levenshtein Distance)算法,用于计算两个DNA序列(字符串)之间的最小编辑操作次数。编辑操作包括插入、删除和替换。
逐步分析:
-
初始化与创建二维数组:
m和n分别是dna1和dna2的长度。- 创建了一个大小为
(m+1) x (n+1)的二维数组dp,用来存储从子问题到更大问题的解决方案。dp[i][j]存储的是将dna1的前i个字符转化为dna2的前j个字符所需要的最小编辑操作数。
-
初始化边界条件:
-
第一行和第一列的初始化表示将一个空字符串转化为另一个字符串的编辑距离。也就是说:
dp[i][0] = i表示将dna1的前i个字符转换为空字符串需要i次删除操作。dp[0][j] = j表示将空字符串转换为dna2的前j个字符需要j次插入操作。
-
-
填充
dp数组:-
对每个
i, j,计算编辑操作的代价:insert_cost = dp[i][j-1] + 1表示通过插入一个字符将dna1的前i个字符转化为dna2的前j个字符。delete_cost = dp[i-1][j] + 1表示通过删除一个字符将dna1的前i个字符转化为dna2的前j个字符。replace_cost = dp[i-1][j-1] + (1 if dna1[i-1] != dna2[j-1] else 0)表示替换操作,如果当前字符不相同则增加代价。
-
-
取最小值:
dp[i][j] = min(insert_cost, delete_cost, replace_cost)取插入、删除、替换三者中的最小代价,确保每一步都选择最优操作。
-
返回最终结果:
- 最终的编辑距离结果保存在
dp[m][n]中,表示将dna1转换为dna2的最小编辑操作次数。
- 最终的编辑距离结果保存在
测试:
solution("AGT", "AGCT")返回1,因为只需插入一个字符C。solution("", "ACGT")返回4,因为需要插入 4 个字符。solution("GCTAGCAT", "ACGT")返回5,这是这两个字符串之间的最小编辑距离。
优化:
6.这段代码的时间复杂度为 O(m*n),空间复杂度也是 O(m*n)。对于较长的字符串,可以考虑使用动态规划的滚动数组优化空间复杂度。