用给定的总和(数组切片)找到最宽的子阵列

时间:2015-05-27 10:59:03

标签: java arrays algorithm

我最近在一个修改过的maximum sum subarray problem上跌跌撞撞,这里我们知道总和(让我们说它是S=2),但是我们需要找到一个最长的数组,它产生这个精确的总和(最长的意思是必须有)最多的元素)

所以对于输入数组

A = [1, 0, -1, 1, 1, -1, -1]

我们找到2片: A(0:4)因为sum(1,0,-1,1,1)2A(3:4),因为sum(1,1)也是2

但是A(0:4)子阵列是最长的,因此它的长度5就是答案。

我找到的大多数解决方案都不是O(n),因为它们使用了2个循环而不是一个或一些包用于集合。这种问题的变体甚至可以用O(n)复杂度来解决吗?

我最感兴趣的是用Java编写的解决方案,但不知道要建模的算法。

assert solution(new int[] { 1, 0, -1, 1, 1, -1, -1 }, 2) == 5;

最好的问候

2 个答案:

答案 0 :(得分:2)

也可以在O(n)中完成:

首先,创建一个辅助数组,该数组对数组的每个前缀求和:

sums[i] = arr[0] + arr[1] + ... + arr[i]

以上内容可以在O(n)时间计算。

接下来,创建一个哈希映射Map<Integer,List<Integer>>,其中键表示前缀sum,值是具有此前缀sum的索引列表。伪代码:

Map<Integer,List<Integer>> map = new HashMap<>();
for (int i = 0; i < sums.length; i++) {
   List<Integer> l = map.get(sums[i]);
   if (l == null) { 
       l = new ArrayList<>();
       map.put(sums[i],l);
   }
   l.add(i);
}

现在,迭代sums数组,并为每个元素x检查地图是否包含k个键x-k == S
这是通过检查它是否有一个键k = S-x来完成的,它在哈希映射中是O(1)。

如果有这样的密钥,则获取值列表中的最后一个索引,这也在O(1)中完成,并将其作为候选匹配。

伪代码:

int currentMaxLength = Integer.MIN_VALUE;
for (int i = 0; i < sums.length; i++) { 
   int k = S-sums[i];
   List<Integer> l = map.get(k);
   if (l == null) continue;
   int width = Math.abs(l.getLast() -  i);
   if (width > currentMaxLength) currentMaxLength = width;
}
return currentMaxLength;

这个想法是,如果某些x1,x2有多个匹配,x2-x1 = Sx1,x2是前缀和,那么最长子阵列的候选者是{{{出现1}},以及x1出现的最后一个位置。
对于x2,这是x1在主循环中引用的元素,它始终被视为候选元素。
对于i,您将始终检查x2的最后一次出现,因此它是正确的。

Quicknote :实际代码还需要考虑x2

Java代码:

sums[-1] = 0

答案 1 :(得分:-1)

想想这样的事情:

如果你有一个数组T [A:D]并且T [A:D]的最大子数组是T [B:C],那么你得到下一个元素T [E],因此你需要最大值[A:E]的子数组,这个子数组必须是旧的T [B:C],或T [B:E]。