Facebook Pixel

1508. Range Sum of Sorted Subarray Sums

Problem Description

You are given an array nums containing n positive integers. Your task involves creating a new array by computing sums of all possible continuous subarrays and then finding a specific range sum.

Here's what you need to do:

  1. Generate all continuous subarray sums: For every possible continuous subarray in nums, calculate its sum. This includes:

    • Single elements: nums[0], nums[1], ..., nums[n-1]
    • Two-element subarrays: nums[0]+nums[1], nums[1]+nums[2], ...
    • Three-element subarrays: nums[0]+nums[1]+nums[2], ...
    • And so on, up to the sum of the entire array
  2. Sort these sums: Take all these subarray sums and sort them in non-decreasing order. This creates a new sorted array with n * (n + 1) / 2 elements (since that's how many continuous subarrays exist).

  3. Find the range sum: Given two indices left and right (both 1-indexed), calculate the sum of all elements from position left to position right in the sorted array.

  4. Return the result modulo 10^9 + 7: Since the answer might be very large, return it modulo 10^9 + 7.

For example, if nums = [1, 2, 3, 4]:

  • The continuous subarray sums would be: [1, 2, 3, 4, 3, 5, 7, 6, 9, 10] (representing sums of [1], [2], [3], [4], [1,2], [2,3], [3,4], [1,2,3], [2,3,4], [1,2,3,4])
  • After sorting: [1, 2, 3, 3, 4, 5, 6, 7, 9, 10]
  • If left = 1 and right = 5, the answer would be 1 + 2 + 3 + 3 + 4 = 13
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The problem asks us to work with subarray sums in a sorted manner. The most straightforward way to think about this is to break it down into manageable steps.

First, we need to generate all possible subarray sums. For an array of size n, we know there are exactly n * (n + 1) / 2 subarrays. Why? Because from each starting position i, we can form (n - i) subarrays ending at different positions. This gives us n + (n-1) + (n-2) + ... + 1 = n * (n + 1) / 2 total subarrays.

To generate these sums, we can use a nested loop approach. The outer loop fixes the starting position, and the inner loop extends the subarray one element at a time. As we extend, we can maintain a running sum rather than recalculating from scratch each time. This is efficient because adding one more element to a subarray just means adding that element to the previous sum.

Once we have all the subarray sums, sorting them is straightforward - we just use a standard sorting algorithm. The key insight here is that we don't need to maintain information about which subarray produced which sum; we only care about the values themselves.

Finally, to get the sum of elements from index left to right (1-indexed), we convert to 0-indexed by subtracting 1, then sum up the elements in that range of our sorted array.

The modulo operation 10^9 + 7 is applied at the end to prevent integer overflow, which is a common requirement in competitive programming when dealing with potentially large sums.

This brute force approach works well for the given constraints because generating and sorting n * (n + 1) / 2 elements is computationally feasible for reasonable values of n.

Learn more about Two Pointers, Binary Search, Prefix Sum and Sorting patterns.

Solution Approach

The implementation follows a simulation approach where we generate all subarray sums, sort them, and then calculate the required range sum.

Step 1: Generate all subarray sums

We use a nested loop structure to generate all possible subarray sums:

  • The outer loop with index i iterates from 0 to n-1, representing the starting position of each subarray
  • For each starting position i, we initialize a sum variable s = 0
  • The inner loop with index j iterates from i to n-1, representing the ending position of each subarray
  • As we extend the subarray by including nums[j], we update the running sum: s += nums[j]
  • Each computed sum s is appended to our result array arr

This approach efficiently computes all subarray sums in O(n^2) time by maintaining a running sum instead of recalculating from scratch for each subarray.

Step 2: Sort the array

Once we have all n * (n + 1) / 2 subarray sums in arr, we sort them using Python's built-in sort() method, which typically uses Timsort with O(k log k) complexity where k = n * (n + 1) / 2.

Step 3: Calculate the range sum

Since the problem uses 1-based indexing while Python uses 0-based indexing, we need to adjust:

  • The element at position left (1-indexed) corresponds to index left - 1 in our array
  • The element at position right (1-indexed) corresponds to index right - 1 in our array

We use Python's slicing notation arr[left - 1 : right] to extract elements from index left - 1 to right - 1 inclusive (note that the end index in Python slicing is exclusive).

Step 4: Apply modulo

Finally, we sum all elements in the extracted range and apply modulo 10^9 + 7 to prevent overflow and meet the problem's requirements.

The overall time complexity is O(n^2 log n) dominated by the sorting step, and the space complexity is O(n^2) for storing all subarray sums.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a small example with nums = [1, 2, 3], left = 2, and right = 5.

Step 1: Generate all subarray sums

We iterate through all possible subarrays:

  • Start at index 0 (value 1):
    • Subarray [1]: sum = 1
    • Subarray [1,2]: sum = 1+2 = 3
    • Subarray [1,2,3]: sum = 1+2+3 = 6
  • Start at index 1 (value 2):
    • Subarray [2]: sum = 2
    • Subarray [2,3]: sum = 2+3 = 5
  • Start at index 2 (value 3):
    • Subarray [3]: sum = 3

This gives us the array of sums: [1, 3, 6, 2, 5, 3]

Step 2: Sort the array

Sorting [1, 3, 6, 2, 5, 3] gives us: [1, 2, 3, 3, 5, 6]

Step 3: Calculate the range sum

With left = 2 and right = 5 (1-indexed):

  • Convert to 0-indexed: we need elements from index 1 to index 4
  • Extract elements: arr[1:5] = [2, 3, 3, 5]
  • Sum these elements: 2 + 3 + 3 + 5 = 13

Step 4: Apply modulo

13 % (10^9 + 7) = 13

The final answer is 13.

This example demonstrates how the solution systematically generates all possible subarray sums using nested loops with a running sum optimization, sorts them to create an ordered list, and then extracts the required range to compute the final result.

Solution Implementation

1class Solution:
2    def rangeSum(self, nums: List[int], n: int, left: int, right: int) -> int:
3        # List to store all possible subarray sums
4        subarray_sums = []
5      
6        # Generate all possible subarray sums
7        # For each starting position i
8        for i in range(n):
9            current_sum = 0
10            # For each ending position j starting from i
11            for j in range(i, n):
12                # Add current element to running sum
13                current_sum += nums[j]
14                # Store the sum of subarray from index i to j
15                subarray_sums.append(current_sum)
16      
17        # Sort all subarray sums in non-decreasing order
18        subarray_sums.sort()
19      
20        # Define modulo constant to prevent integer overflow
21        MOD = 10**9 + 7
22      
23        # Calculate sum of elements from index (left-1) to (right-1) inclusive
24        # Note: left and right are 1-indexed, so we convert to 0-indexed
25        result = sum(subarray_sums[left - 1:right]) % MOD
26      
27        return result
28
1class Solution {
2    public int rangeSum(int[] nums, int n, int left, int right) {
3        // Calculate total number of subarrays: n + (n-1) + ... + 1 = n*(n+1)/2
4        int totalSubarrays = n * (n + 1) / 2;
5        int[] subarraySums = new int[totalSubarrays];
6      
7        // Generate all possible subarray sums
8        int index = 0;
9        for (int startIndex = 0; startIndex < n; startIndex++) {
10            int currentSum = 0;
11            // For each starting position, calculate sums of all subarrays starting from that position
12            for (int endIndex = startIndex; endIndex < n; endIndex++) {
13                currentSum += nums[endIndex];
14                subarraySums[index] = currentSum;
15                index++;
16            }
17        }
18      
19        // Sort all subarray sums in ascending order
20        Arrays.sort(subarraySums);
21      
22        // Calculate the sum of elements from position 'left' to 'right' (1-indexed)
23        int result = 0;
24        final int MODULO = (int) 1e9 + 7;
25      
26        // Convert to 0-indexed and sum the required range
27        for (int i = left - 1; i < right; i++) {
28            result = (result + subarraySums[i]) % MODULO;
29        }
30      
31        return result;
32    }
33}
34
1class Solution {
2public:
3    int rangeSum(vector<int>& nums, int n, int left, int right) {
4        // Calculate total number of subarrays: n*(n+1)/2
5        int totalSubarrays = n * (n + 1) / 2;
6      
7        // Array to store all subarray sums
8        vector<int> subarraySums(totalSubarrays);
9      
10        // Generate all possible subarray sums
11        int index = 0;
12        for (int startIdx = 0; startIdx < n; ++startIdx) {
13            int currentSum = 0;
14            // For each starting position, calculate sums of all subarrays starting from it
15            for (int endIdx = startIdx; endIdx < n; ++endIdx) {
16                currentSum += nums[endIdx];
17                subarraySums[index++] = currentSum;
18            }
19        }
20      
21        // Sort all subarray sums in ascending order
22        sort(subarraySums.begin(), subarraySums.end());
23      
24        // Calculate the sum of elements from position 'left' to 'right' (1-indexed)
25        int result = 0;
26        const int MOD = 1e9 + 7;
27      
28        for (int i = left - 1; i < right; ++i) {
29            result = (result + subarraySums[i]) % MOD;
30        }
31      
32        return result;
33    }
34};
35
1/**
2 * Calculates the sum of subarray sums within a specified range after sorting
3 * @param nums - The input array of numbers
4 * @param n - The length of the input array
5 * @param left - The left boundary (1-indexed) of the range to sum
6 * @param right - The right boundary (1-indexed) of the range to sum
7 * @returns The sum of subarray sums from position left to right, modulo 10^9 + 7
8 */
9function rangeSum(nums: number[], n: number, left: number, right: number): number {
10    // Calculate total number of subarrays: n + (n-1) + ... + 1 = n*(n+1)/2
11    const totalSubarrays: number = (n * (n + 1)) / 2;
12  
13    // Initialize array to store all subarray sums
14    const subarraySums: number[] = new Array(totalSubarrays).fill(0);
15  
16    // Modulo value to prevent integer overflow
17    const MOD: number = 10 ** 9 + 7;
18
19    // Generate all subarray sums
20    let index: number = 0;
21    for (let startIndex: number = 0; startIndex < n; startIndex++) {
22        let currentSum: number = 0;
23      
24        // Calculate sum for all subarrays starting at startIndex
25        for (let endIndex: number = startIndex; endIndex < n; endIndex++) {
26            currentSum += nums[endIndex];
27            subarraySums[index] = currentSum;
28            index++;
29        }
30    }
31
32    // Sort subarray sums in ascending order
33    subarraySums.sort((a: number, b: number) => a - b);
34  
35    // Extract the range from left to right (converting from 1-indexed to 0-indexed)
36    const selectedSums: number[] = subarraySums.slice(left - 1, right);
37  
38    // Calculate the sum of selected elements with modulo operation
39    const result: number = selectedSums.reduce(
40        (accumulator: number, currentValue: number) => (accumulator + currentValue) % MOD, 
41        0
42    );
43  
44    return result;
45}
46

Time and Space Complexity

Time Complexity: O(n² × log n)

The time complexity breaks down as follows:

  • The nested loops (lines 4-8) iterate through all possible subarrays. The outer loop runs n times, and for each iteration i, the inner loop runs (n - i) times. This generates a total of n + (n-1) + (n-2) + ... + 1 = n(n+1)/2 subarrays, which is O(n²) operations.
  • Each subarray sum is appended to the array, taking O(1) time per append.
  • After generating all subarray sums, the array is sorted (line 9), which takes O(k × log k) where k is the number of elements. Since we have n(n+1)/2 elements, this is O(n² × log n²) = O(n² × 2log n) = O(n² × log n).
  • The final sum operation (line 11) takes at most O(n²) time in the worst case when summing all elements.

The dominant operation is the sorting step, giving us a total time complexity of O(n² × log n).

Space Complexity: O(n²)

The space complexity is determined by:

  • The arr list stores all possible subarray sums. With n(n+1)/2 subarrays, this requires O(n²) space.
  • The sorting operation may use additional O(log n) to O(n²) space depending on the implementation, but this doesn't exceed O(n²).
  • Other variables (s, i, j, mod) use constant O(1) space.

Therefore, the overall space complexity is O(n²).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Forgetting to Apply Modulo Operation

One of the most common mistakes is forgetting to apply the modulo operation or applying it incorrectly. The problem explicitly requires the result to be returned modulo 10^9 + 7.

Incorrect approach:

# Forgetting modulo entirely
result = sum(subarray_sums[left - 1:right])
return result

# Or applying modulo too early (though less critical in Python)
for i in range(n):
    current_sum = 0
    for j in range(i, n):
        current_sum = (current_sum + nums[j]) % MOD  # Unnecessary here
        subarray_sums.append(current_sum)

Why it matters: For large arrays with large values, the sum can exceed the maximum integer limit in some languages. While Python handles big integers automatically, the problem requires modulo for consistency and to match expected output format.

Solution: Always apply modulo to the final result as specified in the problem.

2. Index Confusion Between 0-based and 1-based

The problem uses 1-based indexing for left and right, but Python uses 0-based indexing. This is a frequent source of errors.

Incorrect approaches:

# Using indices directly without conversion
result = sum(subarray_sums[left:right]) % MOD  # Wrong!

# Off-by-one error in slicing
result = sum(subarray_sums[left:right + 1]) % MOD  # Wrong!

Why it matters: These mistakes will either include wrong elements or miss required elements, leading to incorrect results.

Solution: Remember that for 1-based indices:

  • Position left maps to index left - 1
  • Position right maps to index right - 1
  • Python slicing [start:end] includes start but excludes end, so use [left-1:right]

3. Inefficient Subarray Sum Calculation

A naive approach might recalculate the entire sum for each subarray from scratch:

Inefficient approach:

subarray_sums = []
for i in range(n):
    for j in range(i, n):
        # Recalculating sum from scratch each time
        subarray_sum = sum(nums[i:j+1])  # O(n) operation inside nested loops!
        subarray_sums.append(subarray_sum)

Why it matters: This adds an extra O(n) factor to the time complexity, making it O(n³) instead of O(n²), which can cause timeouts for large inputs.

Solution: Use a running sum approach as shown in the correct implementation, where you maintain and update the sum incrementally as you extend the subarray.

4. Memory Issues with Very Large Arrays

While less common, for extremely large values of n, storing all O(n²) subarray sums might cause memory issues.

Potential issue:

# For n = 10,000, this creates an array with 50,005,000 elements
subarray_sums = []  # Could consume significant memory

Solution: For most competitive programming problems, this is acceptable. However, if memory becomes an issue, consider:

  • Using a generator-based approach if only specific ranges are needed
  • Implementing a more space-efficient algorithm using heaps for finding k-th smallest elements
  • Note that for this specific problem, we need all sums sorted, so the space usage is generally unavoidable
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What's the output of running the following function using input [30, 20, 10, 100, 33, 12]?

1def fun(arr: List[int]) -> List[int]:
2    import heapq
3    heapq.heapify(arr)
4    res = []
5    for i in range(3):
6        res.append(heapq.heappop(arr))
7    return res
8
1public static int[] fun(int[] arr) {
2    int[] res = new int[3];
3    PriorityQueue<Integer> heap = new PriorityQueue<>();
4    for (int i = 0; i < arr.length; i++) {
5        heap.add(arr[i]);
6    }
7    for (int i = 0; i < 3; i++) {
8        res[i] = heap.poll();
9    }
10    return res;
11}
12
1class HeapItem {
2    constructor(item, priority = item) {
3        this.item = item;
4        this.priority = priority;
5    }
6}
7
8class MinHeap {
9    constructor() {
10        this.heap = [];
11    }
12
13    push(node) {
14        // insert the new node at the end of the heap array
15        this.heap.push(node);
16        // find the correct position for the new node
17        this.bubble_up();
18    }
19
20    bubble_up() {
21        let index = this.heap.length - 1;
22
23        while (index > 0) {
24            const element = this.heap[index];
25            const parentIndex = Math.floor((index - 1) / 2);
26            const parent = this.heap[parentIndex];
27
28            if (parent.priority <= element.priority) break;
29            // if the parent is bigger than the child then swap the parent and child
30            this.heap[index] = parent;
31            this.heap[parentIndex] = element;
32            index = parentIndex;
33        }
34    }
35
36    pop() {
37        const min = this.heap[0];
38        this.heap[0] = this.heap[this.size() - 1];
39        this.heap.pop();
40        this.bubble_down();
41        return min;
42    }
43
44    bubble_down() {
45        let index = 0;
46        let min = index;
47        const n = this.heap.length;
48
49        while (index < n) {
50            const left = 2 * index + 1;
51            const right = left + 1;
52
53            if (left < n && this.heap[left].priority < this.heap[min].priority) {
54                min = left;
55            }
56            if (right < n && this.heap[right].priority < this.heap[min].priority) {
57                min = right;
58            }
59            if (min === index) break;
60            [this.heap[min], this.heap[index]] = [this.heap[index], this.heap[min]];
61            index = min;
62        }
63    }
64
65    peek() {
66        return this.heap[0];
67    }
68
69    size() {
70        return this.heap.length;
71    }
72}
73
74function fun(arr) {
75    const heap = new MinHeap();
76    for (const x of arr) {
77        heap.push(new HeapItem(x));
78    }
79    const res = [];
80    for (let i = 0; i < 3; i++) {
81        res.push(heap.pop().item);
82    }
83    return res;
84}
85

Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More