计算给定总和为零的所有连续子数组

Posted

技术标签:

【中文标题】计算给定总和为零的所有连续子数组【英文标题】:Counting all contiguous sub-arrays given sum zero 【发布时间】:2014-12-19 09:28:15 【问题描述】:

给定长度为 n 的随机数(正数和负数)数组,我想要总和为零的连续子数组。

示例: 假设我有数组a=1, -1 ,2 , -2, 6, -6,输出将是6,因为子数组如下:

1 -1 & 2 -2 & 6 -6 & 1 -1 2 -2 & 2 -2 6 -6 & 1 -1 2 -2 6 -6

我知道强力解 O(n^2),我想要任何其他解 O(n) 或 O(n log n)?

【问题讨论】:

我不认为contiguous sub-arrays sum 问题有O(n), or O(n log n) 解决方案.. 由于可能的子数组的总数是 O(n^2),我怀疑你会想出一个比这更好的通用算法。如果你这样做,它必须是一种不仅仅枚举子数组的方法。考虑这个困难的情况:6,0,0,...0,0,-6。 考虑 0, 0, ... 0, 0 ,现在所有的总和都为零,你必须输出很多东西。 仅供参考:我认为@DavidEisenstat 证明我错了,如下。 【参考方案1】:

设数组为 a1, ..., an。令 s0, ..., sn 为 sj = a1 + ... + aj 定义的前缀和(并且 s0 = 0)。从 i 到 j 的子数组的和是 sj - si-1。对于 O(n)/O(n log n) 时间算法,使用映射计算前缀和中每个数字的出现次数。求和 k 为这张地图的值中的 k 选择 2。

例如,如果我们有你的数组

1 -1 2 -2 6 -6

那么前缀和是

0 1 0 2 0 6 0

计数是

0: 4
1: 1
2: 1
3: 1

输出是 4 选择 2 + 1 选择 2 + 1 选择 2 + 1 选择 2 = 6(其中 k 选择 2 = k(k-1)/2)。

在 Python 中:

def countsumzero(lst):
    prefixsums = [0]
    for x in lst:
        prefixsums.append(prefixsums[-1] + x)
    freq = 
    for y in prefixsums:
        if y in freq:
            freq[y] += 1
        else:
            freq[y] = 1
    return sum(v*(v-1) // 2 for v in freq.values())

【讨论】:

+1:哇,太好了!我认为没有办法将它们全部列举出来,但这似乎确实有效。 我不明白得到前缀和之后的下一步是什么? @DavidEisenstat 我不明白 sum(v*(v-1) // 2 for v in freq.values()) 背后的原因 一切正常。但是最后一行是什么“输出是 4 选择 2 + 1 选择 2 + 1 选择 2 + 1 选择 2 = 6(其中 k 选择 2 = k(k- 1)/2)。”意思是。这个不清楚。

以上是关于计算给定总和为零的所有连续子数组的主要内容,如果未能解决你的问题,请参考以下文章

均值舍入为零的子数组的数量

给定一个数组,打印所有可能的连续子序列,其总和可被给定数 x 整除

谷歌面试:在给定的整数数组中找到所有连续的子序列,其总和在给定范围内。我们能比 O(n^2) 做得更好吗?

素数长度的所有连续子数组的最大和

给定一个整数数组 nums 和一个整数 k,返回总和等于 k ​​的连续子数组的总数

最大连续子数组(元素数量最多)