Solution: Range Sum of Sorted Subarray Sums

Let’s solve the Range Sum of Sorted Subarray Sums problem using the Sort and Search pattern.

Statement

You are given an integer array nums containing nn positive integers along with left and right. Calculate the sum of its elements for every non-empty continuous subarray of nums. Collect these sums into a new array and sort it in nondecreasing order. This will result in a new array of size n×(n+1)/2n \times (n + 1) /2.

Your task is to return the sum of the elements in this sorted array from the index left to right (inclusive with 1-based indexing).

Note: As the result can be large, return the sum modulo 109+710^9 + 7.

Constraints:

  • n=n = nums.length

  • 11 \leq nums.length 1000\leq 1000

  • 11 \leq nums[i] 100\leq 100

  • 11 \leq left \leq right n×(n+1)/2\leq n \times (n + 1) / 2

Solution

In a brute-force approach, you would generate all possible subarrays, calculate their sums, and sort these sums to find the range sum between the indexes left and right. However, this method is inefficient as the number of subarrays grows quadratically with the size of the array.

Instead, we optimize the process using binary search to directly find the range sum without calculating or sorting all the subarray sums. We start by identifying a feasible range for possible subarray sums, which lies between the smallest element in the array, min(nums), and the total sum of the array, sum(nums). This range is necessary as it includes every possible subarray sum, ensuring no valid sums are overlooked and serving as the foundation for the binary search.

For each threshold, we calculate the count of subarrays with sums below the threshold and their cumulative sum using a sliding window technique. Why do we apply binary search? The goal is to find a threshold value TT such that there are exactly kk subarrays with sums less than or equal to TT. The binary search narrows this threshold by checking for each mid value (potential sum threshold) if it satisfies the condition of having at least kk subarrays below it. By adjusting the search range based on the count, we pinpoint the boundaries of the desired range of subarray sums.

Once we calculate the total sum of subarrays up to index right and the total sum of subarrays up to index left - 1, we subtract these two sums to get the sum of the subarrays in the range [left, right].

Following are the implementation steps of the algorithm—categorized into three main sections:

  1. Finding the threshold for k smallest subarray sums:

    1. We start by determining the cutoff value that separates the smallest kk subarray sums from the rest. We do this by defining the minimum possible sum, minSum, as the smallest element in the array and the maximum sum, maxSum, as the total sum of all elements in the array.

    2. Next, we perform a binary search:

      1. We compute the midpoint of the current range by: mid=TLeft+(TRightTLeft)//2\text{mid} = \text{TLeft} + (\text{TRight} - \text{TLeft}) // 2

      2. Next, we use the countAndSum function to find:

        1. The number of subarray sums, count, that is less than or equal to this midpoint.

        2. The cumulative sum, totalSum, of these subarrays.

      3. We then adjust the search range based on the count of subarray sums:

        1. If there are at least kk valid subarrays, we reduce the upper bound, TRight, to focus on smaller sums by assigning mid - 1 to TRight.

        2. Otherwise, increase the lower bound, TLeft, to consider larger sums by assigning mid + 1 to TLeft.

        3. This process continues until the range converges, pinpointing the boundaries of the desired range.

    3. After the binary search converges, use the countAndSum function again to refine the sum of exactly the first subarray sums.

  2. Count and sum subarray sums using the sliding window:

    1. To avoid generating all subarray sums explicitly, we use a sliding window approach:

      1. We maintain a running sum, currentSum, and the cumulative contribution of subarray sums, windowSum, for the current window.

      2. For every new element, nums[j], in the array:

        1. Add it to the running sum and update the window sum using: windowSum+=nums[j](ji+1)\text{windowSum} += \text{nums}[j] * (j - i + 1).

        2. If the running sum exceeds the given threshold, i,e., if currentSum > target, we shrink the window from the left by subtracting currentSum from windowSum until it is valid again.

          1. Remove nums[i] from currentSum and increment i.

      3. Then, we incrementally add the sums of all valid subarrays ending at the current element to the total.

    2. Next, we return count and totalSum for the given target. This method ensures that we calculate subarray sums dynamically without needing explicit storage.

  3. Computing the result for the desired range:

    1. To get the final sum of subarray sums between indexes left and right:

      1. We call sumOfFirstK(nums, n, right) to compute the sum of subarrays up to the right.

      2. And sumOfFirstK(nums, n, left - 1) to compute the cumulative sum up to left - 1.

      3. Then, we subtract these two results to get the sum of all subarray sums within the desired range.

    2. Finally, we return the result modulo to handle potential overflow.

Let’s look at the following illustration to get a better understanding of the solution:

Press + to interact
canvasAnimation-image
1 of 18

Let’s implement the algorithm as discussed above:

import java.util.*;
public class Solution {
static int mod = (int) 1e9 + 7;
public static int rangeSum(int[] nums, int n, int left, int right) {
long result = (sumOfFirstK(nums, n, right) - sumOfFirstK(nums, n, left - 1)) % mod;
return (int) ((result + mod) % mod);
}
private static long sumOfFirstK(int[] nums, int n, int k) {
int minSum = Arrays.stream(nums).min().orElse(0);
int maxSum = Arrays.stream(nums).sum();
int TLeft = minSum, TRight = maxSum;
while (TLeft <= TRight) {
int mid = TLeft + (TRight - TLeft) / 2;
if (countAndSum(nums, n, mid)[0] >= k) {
TRight = mid - 1;
} else {
TLeft = mid + 1;
}
}
long[] result = countAndSum(nums, n, TLeft);
long count = result[0];
long totalSum = result[1];
return totalSum - TLeft * (count - k);
}
private static long[] countAndSum(int[] nums, int n, int target) {
int count = 0;
long currentSum = 0, totalSum = 0, windowSum = 0;
for (int j = 0, i = 0; j < n; ++j) {
currentSum += nums[j];
windowSum += nums[j] * (j - i + 1);
while (currentSum > target) {
windowSum -= currentSum;
currentSum -= nums[i++];
}
count += j - i + 1;
totalSum += windowSum;
}
return new long[]{count, totalSum};
}
public static void main(String[] args) {
int[][] numsArray = {
{1, 2, 3, 4},
{3, 7, 2},
{5},
{8, 1, 4, 2},
{10, 20, 30}
};
int[] nArray = {4, 3, 1, 4, 3};
int[] leftArray = {1, 2, 1, 4, 1};
int[] rightArray = {5, 6, 1, 8, 4};
for (int idx = 0; idx < numsArray.length; idx++) {
int[] nums = numsArray[idx];
int n = nArray[idx];
int left = leftArray[idx];
int right = rightArray[idx];
int result = rangeSum(nums, n, left, right);
System.out.println((idx + 1) + ".\tnums: " + Arrays.toString(nums));
System.out.println("\tn: " + n + ", left: " + left + ", right: " + right);
System.out.println("\tRange sum of subarrays: " + result);
System.out.println(new String(new char[100]).replace("\0", "-"));
}
}
}
Range Sum of Sorted Subarray Sums

Time complexity

The size of the search range directly impacts the number of iterations in the binary search:

  • The search range spans from min(nums)\text{min(nums)} to sum(nums)\text{sum(nums)}.

  • Binary search reduces this range logarithmically, so the number of steps in the binary search is proportional to O(log(sum(nums)min(nums)))O(\log (\text{sum(nums)} - \text{min(nums)})).

  • As sum(nums)\text{sum(nums)} dominates min(nums)\text{min(nums)}, this is simplified to O(log(sum(nums)))O(\log (\text{sum(nums)})).

The sliding window traverses the array once for each binary search step, and for an array of size nn, this takes O(n)O(n) per step.

Thus, the time complexity of the solution is O(nlog(sum))O(n \log (\text{sum})), where nn is the length of the nums array and sum\text {sum} is the total sum of the nums array.

Space complexity

The solution’s space complexity is O(1)O(1) as no additional data structures except variables are used.

Level up your interview prep. Join Educative to access 70+ hands-on prep courses.