-->

Sunday, April 2, 2017

[LeetCode]: 327. Count of Range Sum

https://leetcode.com/problems/count-of-range-sum/#/description

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:
  1. class Solution {
  2. public:
  3.     int countRangeSum(vector<int>& nums, int lower, int upper) {
  4.         int res = 0;
  5.         int n = nums.size();
  6.         if(n==0) return res;
  7.         vector<long> sums;
  8.         sums.push_back(0);
  9.         long sum = 0;
  10.         for(int i=0; i<nums.size(); ++i){
  11.             sum += nums[i];
  12.             int left = findLeftPosition(sums, sum-upper);
  13.             int right = findRightPosition(sums, sum-lower);
  14.             res += right - left;
  15.             int ind = findLeftPosition(sums, sum);
  16.             sums.insert(sums.begin()+ind, sum);
  17.         }
  18.         return res;
  19.     }
  20. private:
  21.     int findLeftPosition(vector<long> &s, long a){
  22.         int left = 0, right = s.size();
  23.         while(left < right){
  24.             int m = left + (right - left)/2;
  25.             if(s[m]<a) left = m+1;
  26.             else right = m;
  27.         }
  28.         return left;
  29.     }
  30.     int findRightPosition(vector<long> &s, long a){
  31.         int left = 0, right = s.size();
  32.         while(left < right){
  33.             int m = left + (right - left)/2;
  34.             if(s[m]<=a) left = m + 1;
  35.             else right = m;
  36.         }
  37.         return left;
  38.     }
  39. };
The code can be further simplified by using lower_bound() and upper_bound() functions. See below: Code:
  1. class Solution {
  2. public:
  3. int countRangeSum(vector<int>& nums, int lower, int upper) {
  4. int res = 0, n = nums.size();
  5. vector<long> sums;
  6. sums.push_back(0);
  7. long sum = 0;
  8. for(int i=0; i<nums.size(); ++i){
  9. sum += nums[i];
  10. auto left = lower_bound(sums.begin(), sums.end(), sum-upper);
  11. auto right = upper_bound(sums.begin(), sums.end(), sum-lower);
  12. res += (int) (right - left);
  13. sums.insert(lower_bound(sums.begin(), sums.end(), sum), sum);
  14. }
  15. return res;
  16. }
  17. };

No comments :

Post a Comment