我最近在一个修改过的maximum sum subarray problem上跌跌撞撞,这里我们知道总和(让我们说它是S=2
),但是我们需要找到一个最长的数组,它产生这个精确的总和(最长的意思是必须有)最多的元素)
所以对于输入数组
A = [1, 0, -1, 1, 1, -1, -1]
我们找到2片:
A(0:4)
因为sum(1,0,-1,1,1)
是2
和A(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;
最好的问候
答案 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 = S
,x1,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]。