题目: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 = -2
, upper = 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; }