[LeetCode]Count of Range Sum

题目:Count of Range Sum

Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusive.
Range sum S(i, j) is defined as the sum of the elements in nums between indices i and j (i ≤ j), inclusive.

Note:
A naive algorithm of O(n2) is trivial. You MUST do better than that.

Example:
Given nums = [-2, 5, -1]lower = -2upper = 2,
Return 3.
The three ranges are : [0, 0][2, 2][0, 2] and their respective sums are: -2, -1, 2.

题意:

给定一个整数数组和一个范围,找到该数组的子集(要求子集是数组中连续的数)中满足子集元素的和在给定范围内的所有子集的个数。

分析:

正常的思路,需要找数组的不同的子集,复杂度为O(n^2)。

/**
动态规划:S[i][j]表示nums中从i到j的和
S[i][j] = S[i][j - 1] + nums[j]
**/
int LeetCode::countRangeSum(vector<int>& nums, int lower, int upper){
    int n = nums.size();
    vector<vector<int>> sum(n,vector<int>(n, 0));
    int count = 0;
    for (size_t i = 0; i < n; i++){
        sum[i][i] = nums[i];
        if (sum[i][i] >= lower && sum[i][i] <= upper)++count;
    }
    for (size_t i = 0; i < n; i++){
        for (size_t j = i + 1; j < n; j++){
            sum[i][j] = sum[i][j - 1] + nums[j];
            if (sum[i][j] >= lower && sum[i][j] <= upper)++count;
        }
    }
    return count;
}

但是,它可以在O(nlogn)的复杂度中完成。

设S(n)表示前n个元素的和,SubSet(i,j)表示数组i到j的子集的和;给定的范围是[low,up];

则简单来讲,要找满足low<=SubSet(i,j)<=up的SubSet的个数。

SubSet(i,j) = S(j) - S(i);也就是要求low<=S(j) - S(i)<=up中ij的个数。

这是可以看出必然需要一个辅助数组保存前n个元素的和;

简化要求来思考:当访问到新的元素j时,得到前j项和,则此时如果是只需求以j为终点的子集中满足题目要求的和的子集个数该怎么求呢?

只需要遍历前j-1个前n项和然后用S(j)-S(k),判断是否在范围内即可;

进一步,如果前j-1个前n项和是有序的,就只要找到第一个满足下界和最后一个满足上界的,然后他们之间包含多少个元素就有多少个满足要求的子集。

这样就要求按大小顺序来存储前n项和,而且可以使用lower_bound()和upper_bound()函数来确定范围,于是就可以想到用multiset来存储前n项和。

那么要找到所有的数组中子集满足条件的个数,就需要将上面求的每个位置的子集的数量加起来即可。

/**
一个基本的想法是使用multiset保存前i个元素的和.因为set是有序的可重复的容器.
当前位置为i时的满足给定范围的序列应该是:lower=< sum[i]-sum[j]<= upper.它对应的范围是[j,i].
所以只需要计算j的个数:使得j (0=< j< i) 满足 sum[i]-upper=< sum[j]<= sum[i]-lower.
STL提供的函数upper_bound, lower_bound能够快速找到这样的j的范围,因为set有序范围内的一定满足上面的条件.
set使用红黑树来实现,因此上面的操作时间复杂度O(logN).
所以总的时间复杂度为O(NlogN).
**/
int LeetCode::countRangeSum(vector<int>& nums, int lower, int upper){
    multiset<long long>sums;//注意使用long long防止溢出
    long long sum = 0L;
    int result = 0;
    sums.insert(0);//插入第0个和
    for (size_t i = 0; i < nums.size(); i++){
        sum += nums[i];
        result += distance(sums.lower_bound(sum - upper), sums.upper_bound(sum - lower));//找到满足sum[i]-upper=< sum[j]<= sum[i]-lower的范围
        sums.insert(sum);
    }
    return result;
}
原文地址:https://www.cnblogs.com/yeqluofwupheng/p/6959377.html