有Levenshtein distance 的概念。它用于比较两个字符串(但是您也可以将相同的原则应用于整数数组):将一个字符串更改为另一个字符串的最少编辑次数,其中一次编辑是删除、插入或替换。
使用动态规划可以有效地解决问题。 wiki 文章展示了这种方法。
您可以对您的问题使用相同的方法。但是由于在您的情况下您不允许替换元素(仅删除和插入操作),您甚至可以简化递归(微小)位:
def edit_distance(s, len_s, t, len_t):
if len_s == 0:
return len_t
if len_t == 0:
return len_s
if s[len_s-1] == t[len_t-1]:
return edit_distance(s, len_s-1, t, len_t-1)
else:
return min(edit_distance(s, len_s-1, t, len_t) + 1,
edit_distance(s, len_s, t, len_t-1) + 1)
上面的代码没有动态编程。为了提高效率,您需要添加它。
此外,代码只会计算步数。如果您还想列出步骤,则必须存储完整的表格并回溯解决方案。
该方法的时间和内存复杂度:O(len_s * len_t)。
这是一个使用您的两个数组[1, 4, 6, 12, 44] 和[2, 4, 6, 44, 45] 的示例。如果您对字符串的每个可能的前缀组合应用动态编程(例如,使用底部优先方法),您会得到一个表格。
0 1 2 3 4 5
1 2 3 4 5 6
2 3 2 3 4 5
3 4 3 2 3 4
4 5 4 3 4 5
5 6 5 4 3 4
在右下角,我们看到 4 是使两个数组相等的最佳步数。现在我们可以回溯并再次查看递归公式。由于最后两个元素不相等,因此它必须是插入/删除操作。我们可以在表格中看到[1, 4, 6, 12], [2, 4, 6, 44, 45] 的最佳步数是5,[1, 4, 6, 12, 44], [2, 4, 6, 44] 的最佳步数是3。所以这里的最佳做法是删除第二个数组的最后一个元素,或者换句话说,在第一个数组中插入45。
现在我们可以处理导致[1, 4, 6, 12, 44], [2, 4, 6, 44] 的最后一步。由于最后一个元素相等,因此步骤很清楚。我们将它们都留下,不执行插入或删除操作。
那么[1, 4, 6, 12], [2, 4, 6] 的最后一步是什么?从表中可以看出,最优值 3 来源于[1, 4, 6], [2, 4, 6] 的位置,这意味着在第一个数组中删除了12。
等等。
有趣的是,可以有多个最佳解决方案。在这里,我向您展示了一种可能的路径(与您的解决方案完全对应):
0-1 2 3 4 5
|
1 2 3 4 5 6
\
2 3 2 3 4 5
\
3 4 3 2 3 4
|
4 5 4 3 4 5
\
5 6 5 4 3-4