3427. Sum of Variable Length Subarrays
Problem Description
You are given an integer array nums
of size n
. For each index i
in the array (where 0 <= i < n
), you need to define a subarray that ends at index i
.
The subarray for each index i
is defined as nums[start ... i]
where:
start = max(0, i - nums[i])
This means:
- The subarray always ends at index
i
- The starting position is calculated by going back
nums[i]
positions from indexi
, but it cannot go below index0
- If
i - nums[i]
is negative, the subarray starts at index0
- If
i - nums[i]
is non-negative, the subarray starts at indexi - nums[i]
Your task is to calculate the sum of all elements in each of these subarrays and return the total sum.
For example, if nums = [2, 1, 3]
:
- For
i = 0
:start = max(0, 0 - 2) = 0
, subarray isnums[0...0] = [2]
, sum =2
- For
i = 1
:start = max(0, 1 - 1) = 0
, subarray isnums[0...1] = [2, 1]
, sum =3
- For
i = 2
:start = max(0, 2 - 3) = 0
, subarray isnums[0...2] = [2, 1, 3]
, sum =6
- Total sum =
2 + 3 + 6 = 11
Intuition
The key insight is that we need to calculate the sum of elements in multiple subarrays, where each subarray ends at a different index i
and starts at max(0, i - nums[i])
.
A naive approach would be to iterate through each index i
, then iterate from the start position to i
to calculate the sum for each subarray. This would result in O(n²) time complexity.
However, we can optimize this using prefix sums. The idea is that if we know the cumulative sum up to any point in the array, we can calculate the sum of any subarray in O(1) time using the formula:
sum(nums[start...i]) = prefixSum[i+1] - prefixSum[start]
Here's how we arrive at this approach:
- First, we build a prefix sum array where
s[i]
represents the sum of all elements from index0
to indexi-1
- For each index
i
, we need the sum fromstart
toi
, which equalss[i+1] - s[start]
- The starting position is
max(0, i - nums[i])
By precomputing all prefix sums once at the beginning, we can then calculate each subarray sum in constant time. This reduces our overall time complexity from O(n²) to O(n).
The use of accumulate
with initial=0
in Python elegantly creates the prefix sum array where s[0] = 0
(sum of zero elements), s[1] = nums[0]
(sum up to index 0), s[2] = nums[0] + nums[1]
(sum up to index 1), and so on. This indexing makes it convenient to calculate subarray sums using simple subtraction.
Learn more about Prefix Sum patterns.
Solution Approach
The solution implements the prefix sum optimization strategy to efficiently calculate the required subarray sums.
Step 1: Build the Prefix Sum Array
s = list(accumulate(nums, initial=0))
This creates a prefix sum array s
where:
s[0] = 0
(sum of zero elements)s[1] = nums[0]
s[2] = nums[0] + nums[1]
s[i] = nums[0] + nums[1] + ... + nums[i-1]
The initial=0
parameter ensures the array starts with 0, making it convenient for subarray sum calculations.
Step 2: Calculate Subarray Sums
return sum(s[i + 1] - s[max(0, i - x)] for i, x in enumerate(nums))
For each index i
with value x = nums[i]
:
- Calculate the starting position:
start = max(0, i - x)
- Calculate the subarray sum from
start
toi
:s[i + 1] - s[start]
s[i + 1]
gives the cumulative sum up to and including indexi
s[start]
gives the cumulative sum up to but not including indexstart
- Their difference gives the sum of elements from
start
toi
inclusive
Example Walkthrough:
For nums = [2, 1, 3]
:
- Prefix sum array:
s = [0, 2, 3, 6]
- For
i=0, x=2
:start = max(0, 0-2) = 0
, sum =s[1] - s[0] = 2 - 0 = 2
- For
i=1, x=1
:start = max(0, 1-1) = 0
, sum =s[2] - s[0] = 3 - 0 = 3
- For
i=2, x=3
:start = max(0, 2-3) = 0
, sum =s[3] - s[0] = 6 - 0 = 6
- Total =
2 + 3 + 6 = 11
Time Complexity: O(n) - We iterate through the array twice (once for prefix sum, once for calculating results) Space Complexity: O(n) - For storing the prefix sum array
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a detailed example with nums = [3, 1, 2, 4]
.
Step 1: Build the prefix sum array
- Start with
nums = [3, 1, 2, 4]
- Create prefix sum array:
s = [0, 3, 4, 6, 10]
s[0] = 0
(no elements)s[1] = 3
(sum up to index 0)s[2] = 3 + 1 = 4
(sum up to index 1)s[3] = 3 + 1 + 2 = 6
(sum up to index 2)s[4] = 3 + 1 + 2 + 4 = 10
(sum up to index 3)
Step 2: Calculate subarray sum for each index
For i = 0 (nums[0] = 3):
- Start position:
max(0, 0 - 3) = max(0, -3) = 0
- Subarray:
nums[0...0] = [3]
- Sum using prefix:
s[1] - s[0] = 3 - 0 = 3
For i = 1 (nums[1] = 1):
- Start position:
max(0, 1 - 1) = max(0, 0) = 0
- Subarray:
nums[0...1] = [3, 1]
- Sum using prefix:
s[2] - s[0] = 4 - 0 = 4
For i = 2 (nums[2] = 2):
- Start position:
max(0, 2 - 2) = max(0, 0) = 0
- Subarray:
nums[0...2] = [3, 1, 2]
- Sum using prefix:
s[3] - s[0] = 6 - 0 = 6
For i = 3 (nums[3] = 4):
- Start position:
max(0, 3 - 4) = max(0, -1) = 0
- Subarray:
nums[0...3] = [3, 1, 2, 4]
- Sum using prefix:
s[4] - s[0] = 10 - 0 = 10
Step 3: Calculate total sum Total = 3 + 4 + 6 + 10 = 23
The key insight is that by precomputing the prefix sums, we can calculate any subarray sum in O(1) time using the formula s[i+1] - s[start]
, avoiding the need to repeatedly sum the same elements.
Solution Implementation
1from itertools import accumulate
2from typing import List
3
4class Solution:
5 def subarraySum(self, nums: List[int]) -> int:
6 # Create prefix sum array where prefix_sums[i] = sum of nums[0:i]
7 # initial=0 means prefix_sums[0] = 0 (sum of empty array)
8 prefix_sums = list(accumulate(nums, initial=0))
9
10 # Calculate the sum of all special subarrays
11 total_sum = 0
12
13 # For each element at index i with value nums[i]
14 for i, num_value in enumerate(nums):
15 # Calculate the starting index of the subarray
16 # The subarray length is at most (num_value + 1) elements
17 start_index = max(0, i - num_value)
18
19 # Calculate sum of subarray from start_index to i (inclusive)
20 # prefix_sums[i + 1] - prefix_sums[start_index] gives sum(nums[start_index:i+1])
21 subarray_sum = prefix_sums[i + 1] - prefix_sums[start_index]
22
23 # Add this subarray sum to the total
24 total_sum += subarray_sum
25
26 return total_sum
27
1class Solution {
2 public int subarraySum(int[] nums) {
3 int n = nums.length;
4
5 // Create prefix sum array where prefixSum[i] represents
6 // the sum of elements from index 0 to i-1
7 int[] prefixSum = new int[n + 1];
8
9 // Build the prefix sum array
10 // prefixSum[i] = sum of nums[0] to nums[i-1]
11 for (int i = 1; i <= n; i++) {
12 prefixSum[i] = prefixSum[i - 1] + nums[i - 1];
13 }
14
15 // Calculate the total sum of all valid subarrays
16 int totalSum = 0;
17
18 // For each element in the array
19 for (int i = 0; i < n; i++) {
20 // Calculate the starting index of the subarray
21 // The subarray extends from max(0, i - nums[i]) to i
22 int startIndex = Math.max(0, i - nums[i]);
23
24 // Calculate sum of subarray using prefix sums
25 // Sum from startIndex to i = prefixSum[i+1] - prefixSum[startIndex]
26 int subarraySum = prefixSum[i + 1] - prefixSum[startIndex];
27
28 // Add this subarray sum to the total
29 totalSum += subarraySum;
30 }
31
32 return totalSum;
33 }
34}
35
1class Solution {
2public:
3 int subarraySum(vector<int>& nums) {
4 int n = nums.size();
5
6 // Create prefix sum array with size n+1
7 // prefixSum[i] stores the sum of elements from index 0 to i-1
8 vector<int> prefixSum(n + 1);
9
10 // Build prefix sum array
11 // prefixSum[i] = sum of nums[0] to nums[i-1]
12 for (int i = 1; i <= n; ++i) {
13 prefixSum[i] = prefixSum[i - 1] + nums[i - 1];
14 }
15
16 // Calculate the result
17 int result = 0;
18
19 // For each position i, calculate the sum of subarray
20 for (int i = 0; i < n; ++i) {
21 // The subarray starts from max(0, i - nums[i]) and ends at i
22 // This means we look back nums[i] positions from current index i
23 // but not going below index 0
24 int startIndex = max(0, i - nums[i]);
25
26 // Add the sum of subarray [startIndex, i] to the result
27 // Using prefix sum: sum[startIndex, i] = prefixSum[i+1] - prefixSum[startIndex]
28 result += prefixSum[i + 1] - prefixSum[startIndex];
29 }
30
31 return result;
32 }
33};
34
1/**
2 * Calculates the sum of subarrays where each element determines its subarray range
3 * For each index i, the subarray spans from max(0, i - nums[i]) to i
4 * @param nums - Input array of numbers
5 * @returns The total sum of all specified subarrays
6 */
7function subarraySum(nums: number[]): number {
8 const arrayLength: number = nums.length;
9
10 // Build prefix sum array where prefixSum[i] represents sum of elements from index 0 to i-1
11 // prefixSum[0] = 0 (sum of zero elements)
12 const prefixSum: number[] = Array(arrayLength + 1).fill(0);
13 for (let i = 0; i < arrayLength; ++i) {
14 prefixSum[i + 1] = prefixSum[i] + nums[i];
15 }
16
17 let totalSum: number = 0;
18
19 // For each position i, calculate sum of subarray from startIndex to i
20 for (let i = 0; i < arrayLength; ++i) {
21 // Determine the starting index of the subarray
22 // It cannot go below 0, hence max(0, i - nums[i])
23 const startIndex: number = Math.max(0, i - nums[i]);
24
25 // Calculate subarray sum using prefix sum technique
26 // Sum from startIndex to i = prefixSum[i+1] - prefixSum[startIndex]
27 const subarraySum: number = prefixSum[i + 1] - prefixSum[startIndex];
28
29 totalSum += subarraySum;
30 }
31
32 return totalSum;
33}
34
Time and Space Complexity
Time Complexity: O(n)
- The
accumulate
function iterates through the array once to build the prefix sum array, which takesO(n)
time - The list comprehension iterates through each element in
nums
once, performing constant-time operations (s[i + 1] - s[max(0, i - x)]
) for each element, which takesO(n)
time - The
sum
function adds up all the computed values, which also takesO(n)
time - Overall time complexity:
O(n) + O(n) + O(n) = O(n)
Space Complexity: O(n)
- The prefix sum array
s
storesn + 1
elements (including the initial 0), requiringO(n)
space - The list comprehension generates a temporary list of
n
values before summing them, requiringO(n)
space - Overall space complexity:
O(n)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Misunderstanding the Subarray Definition
Issue: A common mistake is misinterpreting how the subarray is defined. Developers might think:
- The subarray has a fixed length of
nums[i]
elements - The subarray starts at index
i
and goes forward - The calculation is
i + nums[i]
instead ofi - nums[i]
Example of Wrong Understanding:
# WRONG: Thinking subarray starts at i and has length nums[i]
def wrong_solution(nums):
total = 0
for i, x in enumerate(nums):
end = min(len(nums), i + x) # Wrong direction!
total += sum(nums[i:end])
return total
Solution: Remember that:
- The subarray always ENDS at index
i
- We go BACKWARDS by
nums[i]
positions to find the start - Formula:
start = max(0, i - nums[i])
Pitfall 2: Off-by-One Errors with Prefix Sum Indexing
Issue: When using prefix sums, it's easy to confuse the indexing, especially when initial=0
is used.
Common Mistakes:
# WRONG: Incorrect prefix sum indexing
prefix_sums = list(accumulate(nums)) # No initial=0
# Now prefix_sums[i] = sum(nums[0:i+1]) instead of sum(nums[0:i])
# Using wrong indices for calculation
subarray_sum = prefix_sums[i] - prefix_sums[start] # Wrong!
Solution: When using accumulate(nums, initial=0)
:
prefix_sums[k]
= sum of firstk
elements- To get sum from index
start
toi
inclusive: useprefix_sums[i+1] - prefix_sums[start]
- Always verify with a small example to ensure correct indexing
Pitfall 3: Not Handling Edge Cases
Issue: Failing to properly handle cases where:
nums[i]
is 0 (subarray is just the single element at indexi
)nums[i]
is greater thani
(subarray starts from index 0)- Array contains negative numbers (if allowed by constraints)
Example of Incomplete Handling:
# WRONG: Might fail for edge cases
def incomplete_solution(nums):
total = 0
for i in range(len(nums)):
start = i - nums[i] # Forgot max(0, ...)!
# This could give negative start index
total += sum(nums[start:i+1])
return total
Solution: Always use max(0, i - nums[i])
to ensure the start index never goes below 0, and test with edge cases like nums = [0, 5, 1]
where the second element would try to go back 5 positions from index 1.
Consider the classic dynamic programming of fibonacci numbers, what is the recurrence relation?
Recommended Readings
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!