求一组子数组的和

Find sum of an array of subarrays

本文关键字:数组 一组      更新时间:2023-10-16

这是2017年谷歌亚太地区的一个问题。问题D: Sum的总和

Alice给她的朋友Bob一个N个正整数的数组,从1到N。她向Bob提出了许多问题,如"这两个索引之间的数字之和是多少?"但是鲍勃解决这个问题太容易了。Alice取她的数组并找到它的所有N*(N+1)/2个非空子数组。她找到每个子数组的和,然后对这些值进行排序(以非递减顺序)以创建一个新的数组,索引从1到N*(N+1)/2。例如,对于初始数组[2,3,2],Alice将生成子数组[2],[3],[2],[2,3],[3,2]和[2,3,2](注意,例如[2,2]不是子数组)。然后,她会取这些和——2,3,2,5,5,7——并对它们进行排序,得到一个新的数组[2,2,3,5,5,7]。Alice给了Bob一个初始数组,以及Q个形式为"新数组中从索引Li到Ri的数字之和是多少?"的查询。现在鲍勃有麻烦了!你能帮帮他吗?

对于大型数据集,直接的解决方案即使在c++中也太低效了。有没有更有效的方法来解决这个问题?

目前我正在通过这个for循环来构造最终的数组:

    multiset<int> sums;
    long long int temp = 0;
    for (long long int len = 1; len <= n; ++len)
    {
        for (int start = 0; start+len <= n; ++start)
        {
            temp = 0;
            for (int i = 0; i < len; ++i)
            {
                temp += arr[start + i]; //arr stores the original array of n digits
            }
            sums.insert(temp);
        }
    }

<罢工> p。S:我现在的实现是O(n^5)吗?我的错误,我现在可以看到它是O(n^3)。谢谢你!编辑:到目前为止的答案很有帮助,但对于涉及n = 200000项的大型数据集,似乎任何预先计算整个子数组数组的解决方案都太昂贵了。所有提交的解决方案似乎都没有计算子数组的整个数组

如评论中所述,您的解决方案是O(N^3),计算为O(N^2)乘以O(N)和在multiset中插入(与O(N)相比,您可以忽略它,参见此答案的底部)。

但是交换你的前两个循环,你在做完全相同的N*(N+1)/2求和和插入:

for (int start = 0; start < n; ++start)
{
    for (long long int len = 1; start + len <= n; ++len)
    {
        temp = 0;
        for (int i = 0; i < len; ++i)
        {
            temp += arr[start + i]; //arr stores the original array of n digits
        }
        sums.insert(temp);
    }
}

现在如果你看一下你的temp和,很明显你在做冗余的工作。从start + 1求和到start + 1,然后从start + 1求和到start + 2,然后从start + 1求和到start + 3,依此类推。对于每个新的len,计算的和是len的前一个值加上一项。因此,你可以删除这个内部循环:

for (int start = 0; start < n; ++start)
{
    temp = 0;
    for (long long int len = 1; start + len <= n; ++len)
    {
        temp += arr[start + len]; //arr stores the original array of n digits
        sums.insert(temp);
    }
}

在N*(N+1)/2中生成了一组值。当然,使用multiset可以隐藏数据排序,但插入通常需要花费log(sums.size())

单独排序,因为对大小为S的集合排序需要S * log(S),所以N*(N+1)/2 * log ( N*(N+1)/2 )的开销(刚好)小于N*(N+1) * log((N+1)/sqrt(2))

注意,因为你有正整数,你用内部循环生成的每一组len整数都已经排序了,所以也许你可以用它们来做一些聪明的事情来加速排序。这也是multiset根据cplusplus.com所做的:

如果插入N个元素,一般为Nlog(size+N),但如果元素已经按照容器使用的相同排序标准排序,则为线性大小+N。

做了一点搜索,我发现了这个,我希望它会有用

https://www.quora.com/how可以-问题- d -总结- -总结-圆- e - - -谷歌亚太区-测试- 2016 -是-解决- - - - - - - -大型数据集

我能想到的最简洁有效的方法是:

std::vector<int> in{ 2, 3, 2 };
std::vector<int> out(in.size()*(in.size()+1)/2);
auto out_it = out.begin();
for (size_t i = 0; i < in.size() ; ++i) {
    out_it=std::partial_sum(in.begin()+i, in.end(), out_it);
}
std::sort(out.begin(), out.end());

除了复杂性的考虑(这个解决方案是-我相信- O(n^2 * log(n)),因为你排序的数组有O(n^2)项),你应该避免动态内存分配和指针追逐像瘟疫(这两者都是std::multi_set的固有部分)。

我的Python 3版本如下。

我没有测试所有的测试用例,但这只是一个实现的想法:

from functools import reduce
import itertools
stuff = [2,3,2]
temp = []
for L in range(1, len(stuff)+1):
    for subset in itertools.combinations(stuff, L):
        if len(subset) > 1:
            if all(subset[0] == x for x in subset):
                continue
        print(subset)
        temp.append(reduce(lambda x,y: x+y, subset))
temp = sorted(temp)
print(temp)
print("all sum : ", reduce(lambda x,y: x+y, temp))