Solution:
This is an interesting question. As stated in the introduction, the O(n2) method is not accepted. So we need to find a faster way. For this question, we are look for ranges that satisfy:
sum[i to j] = sum[0 to j] - sum[0 to i] >= lower; and
sum[i to j] = sum[0 to j] - sum[0 to i] <= upper
which can be further re-wrote as:
sum[0 to i] <= sum[0 to j] - lower; and
sum[0 to i] >= sum[0 to j] - upper.
Now it becomes clear that once we have sum[0 to i] where 0<=i<n (which can be obtained easily with O(n)), we can calculate the elements numbers by just looking the range of [sum[0 to j] - upper, sum[0 to j] - lower]. In order to avoid the re-counting, we can count the number of elements in the process of constructing the sorted array of sum[0 to i]. In this way, all the ranges found have at least one unique end. We also need to do the counting before inserting the element sum[0 to i] into the sorted array, to avoid the case that the sum[0 to i] locates just between [sum[0 to j] - upper, sum[0 to j] - lower]. (in this case, we will mistakenly count the elements by 1 more.)
Some details:
1):We also need to consider the sum[0 to i] itself, which can be elegantly solving by add a '0' element inside the sorted array in the beginning.
2): the long type can be used to avoid overflow problem.
The key point is by constructing a sorted array, the complexity can be reduced from O(n2) to O(nlnn). See code below:
Code:
class Solution {
public:
int countRangeSum(vector<int>& nums, int lower, int upper) {
int res = 0;
int n = nums.size();
if(n==0) return res;
vector<long> sums;
sums.push_back(0);
long sum = 0;
for(int i=0; i<nums.size(); ++i){
sum += nums[i];
int left = findLeftPosition(sums, sum-upper);
int right = findRightPosition(sums, sum-lower);
res += right - left;
int ind = findLeftPosition(sums, sum);
sums.insert(sums.begin()+ind, sum);
}
return res;
}
private:
int findLeftPosition(vector<long> &s, long a){
int left = 0, right = s.size();
while(left < right){
int m = left + (right - left)/2;
if(s[m]<a) left = m+1;
else right = m;
}
return left;
}
int findRightPosition(vector<long> &s, long a){
int left = 0, right = s.size();
while(left < right){
int m = left + (right - left)/2;
if(s[m]<=a) left = m + 1;
else right = m;
}
return left;
}
};
The code can be further simplified by using lower_bound() and upper_bound() functions.
See below:
Code:
class Solution {
public:
int countRangeSum(vector<int>& nums, int lower, int upper) {
int res = 0, n = nums.size();
vector<long> sums;
sums.push_back(0);
long sum = 0;
for(int i=0; i<nums.size(); ++i){
sum += nums[i];
auto left = lower_bound(sums.begin(), sums.end(), sum-upper);
auto right = upper_bound(sums.begin(), sums.end(), sum-lower);
res += (int) (right - left);
sums.insert(lower_bound(sums.begin(), sums.end(), sum), sum);
}
return res;
}
};
No comments :
Post a Comment