用特定命令描述阵列转换

时间:2018-07-25 15:14:32

标签: c# arrays algorithm

假设我们有两个整数数组,例如:

var A = new int[] {1, 4, 6, 12, 44};
var B = new int[] {2, 4, 6, 44, 45};

问题在于将过渡步骤描述为:

从数组A开始,然后继续执行以下操作:

  • 步骤1:remove index = 0 // result = {4, 6, 12, 44}
  • 第2步:insert index = 0 value = 2 // result = {2, 4, 6, 12, 44}
  • 第3步:remove index = 3 // result = {2, 4, 6, 44}
  • 第4步:insert index = 4 value = 45 // result = {2, 4, 6, 44, 45}

现在我们有了数组B

我的问题是:如何以给定数组A和B以编程方式生成这些步骤的任何编程语言或伪代码设计此算法?

常见步骤结构如下:

步骤N:insert/remove index = i [value = v]

当然,从A中删除所有元素,然后从B中插入所有元素是一种解决方案,并且对于给定的A和{{1},也许会有不止一种解决方案},但我希望以最少的步骤进行过渡。

1 个答案:

答案 0 :(得分:0)

Levenshtein distance的概念。它用于比较两个字符串(但是您也可以将相同的原理应用于整数数组):最少的编辑次数即可将一个字符串更改为另一个字符串,其中一个编辑是删除,插入或替换。

使用动态编程可以有效地解决该问题。 Wiki文章显示了这种方法。 您可以对问题使用相同的方法。但是由于在您的情况下不允许您替换元素(仅允许删除和插入操作),因此您甚至可以简化递归a(微小)位:

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