3284. Sum of Consecutive Subarrays
Problem Description
You need to find all consecutive subarrays in an array and calculate the sum of their values.
A subarray is called consecutive if it follows one of these patterns:
- Each element is exactly 1 more than the previous element (increasing by 1), like
[3, 4, 5]
- Each element is exactly 1 less than the previous element (decreasing by 1), like
[9, 8, 7]
The value of an array is simply the sum of all its elements. For example:
[3, 4, 5]
has value 12 (3 + 4 + 5)[9, 8]
has value 17 (9 + 8)[3, 4, 3]
is NOT consecutive (4 to 3 is -1, but 3 to 4 is +1, not consistent)[8, 6]
is NOT consecutive (difference is -2, not -1)
Given an array nums
, you need to:
- Find all possible subarrays that are consecutive
- Calculate the value (sum) of each consecutive subarray
- Return the total sum of all these values
Important notes:
- A single element by itself counts as a consecutive subarray
- The result should be returned modulo
10^9 + 7
to handle large numbers - A subarray is a contiguous part of the array (elements must be adjacent in the original array)
For example, if nums = [1, 2, 3]
:
- Consecutive subarrays are:
[1]
,[2]
,[3]
,[1,2]
,[2,3]
,[1,2,3]
- Their values are: 1, 2, 3, 3, 5, 6
- Total sum = 1 + 2 + 3 + 3 + 5 + 6 = 20
Intuition
The key insight is that we need to track consecutive subarrays efficiently without checking every possible subarray, which would be too slow.
Think about what happens as we traverse the array element by element. At each position, we need to know:
- Which consecutive subarrays can be extended to include the current element
- Which new consecutive subarrays start at the current element
When we move from nums[i-1]
to nums[i]
, there are only three possibilities:
nums[i] - nums[i-1] == 1
: The current element continues an increasing sequencenums[i] - nums[i-1] == -1
: The current element continues a decreasing sequence- Neither: The current element breaks any consecutive pattern
This observation leads us to maintain two separate tracking mechanisms:
- For increasing consecutive subarrays (difference of +1)
- For decreasing consecutive subarrays (difference of -1)
For each type, we need to track:
- Length (
f
for increasing,g
for decreasing): How many consecutive subarrays of this type end at the current position - Sum (
s
for increasing,t
for decreasing): The total sum of all these subarrays
Why track the length? Because when we extend a consecutive sequence by one element, we're not just adding one new subarray - we're adding multiple subarrays. For example, if we have [1, 2]
and add 3
, we get:
- The subarray
[3]
(always counts) - The subarray
[2, 3]
(extending from previous element) - The subarray
[1, 2, 3]
(extending the full sequence)
The number of subarrays we extend equals the length of the current consecutive sequence. Each of these subarrays includes the new element, so we add length × current_element
to our running sum.
By maintaining these values as we traverse the array once, we can efficiently calculate the total sum of all consecutive subarrays without redundant work.
Learn more about Two Pointers and Dynamic Programming patterns.
Solution Approach
We implement the solution using dynamic programming with state tracking as we traverse the array once.
Variables Setup:
f
: Length of the increasing consecutive subarray ending at current positiong
: Length of the decreasing consecutive subarray ending at current positions
: Sum of all increasing consecutive subarrays ending at current positiont
: Sum of all decreasing consecutive subarrays ending at current positionans
: Accumulator for the final answermod = 10^9 + 7
: For modulo operation
Initialization:
- Set
f = g = 1
(single element forms a consecutive subarray of length 1) - Set
s = t = nums[0]
(initial sum is just the first element) - Set
ans = nums[0]
(first element contributes to answer)
Main Algorithm:
For each pair of consecutive elements (x, y)
where x = nums[i-1]
and y = nums[i]
:
-
Check for increasing sequence (
y - x == 1
):- Increment
f
by 1 (extending the increasing sequence length) - Update
s
by addingf * y
(allf
subarrays now includey
) - Add
s
to answer (all increasing subarrays ending aty
) - If not increasing, reset:
f = 1
,s = y
- Increment
-
Check for decreasing sequence (
y - x == -1
):- Increment
g
by 1 (extending the decreasing sequence length) - Update
t
by addingg * y
(allg
subarrays now includey
) - Add
t
to answer (all decreasing subarrays ending aty
) - If not decreasing, reset:
g = 1
,t = y
- Increment
-
Handle non-consecutive case (
abs(y - x) != 1
):- Add
y
to answer (single element forms a consecutive subarray)
- Add
Why the formula s += f * y
works:
When extending an increasing sequence, we have f
different subarrays ending at position i
:
- The subarray of length 1: just
[y]
- The subarray of length 2:
[x, y]
- The subarray of length 3:
[..., x, y]
- And so on...
Each of these f
subarrays includes the new element y
, so we add f * y
to the sum.
Time Complexity: O(n)
- single pass through the array
Space Complexity: O(1)
- only using constant extra variables
The algorithm efficiently computes the sum by maintaining running totals and lengths of consecutive sequences, avoiding the need to explicitly enumerate all subarrays.
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 the solution with nums = [1, 2, 3, 5]
.
Initial State:
- Start with first element:
nums[0] = 1
f = 1, g = 1
(length of increasing/decreasing sequences)s = 1, t = 1
(sum of increasing/decreasing subarrays)ans = 1
(contribution from[1]
)
Step 1: Process nums[1] = 2
- Previous:
x = 1
, Current:y = 2
- Difference:
2 - 1 = 1
(increasing!)
Increasing sequence update:
f = f + 1 = 2
(now have 2 consecutive subarrays:[2]
and[1,2]
)s = s + f * y = 1 + 2 * 2 = 5
(sum of[2]=2
and[1,2]=3
)ans = ans + s = 1 + 5 = 6
Decreasing sequence update:
- Not decreasing, so reset:
g = 1
,t = 2
- No contribution to answer
Step 2: Process nums[2] = 3
- Previous:
x = 2
, Current:y = 3
- Difference:
3 - 2 = 1
(increasing!)
Increasing sequence update:
f = f + 1 = 3
(now have 3 subarrays:[3]
,[2,3]
,[1,2,3]
)s = s + f * y = 5 + 3 * 3 = 14
(sum of[3]=3
,[2,3]=5
,[1,2,3]=6
)ans = ans + s = 6 + 14 = 20
Decreasing sequence update:
- Not decreasing, so reset:
g = 1
,t = 3
- No contribution to answer
Step 3: Process nums[3] = 5
- Previous:
x = 3
, Current:y = 5
- Difference:
5 - 3 = 2
(not consecutive!)
Since it's not consecutive (neither +1 nor -1):
- Reset both sequences:
f = 1
,s = 5
,g = 1
,t = 5
- Add single element:
ans = ans + y = 20 + 5 = 25
Final Answer: 25
Let's verify by listing all consecutive subarrays:
[1]
→ sum = 1[2]
→ sum = 2[3]
→ sum = 3[5]
→ sum = 5[1,2]
→ sum = 3[2,3]
→ sum = 5[1,2,3]
→ sum = 6
Total = 1 + 2 + 3 + 5 + 3 + 5 + 6 = 25 ✓
The algorithm efficiently computes this by tracking running sums and lengths of consecutive sequences, avoiding the need to explicitly check every possible subarray.
Solution Implementation
1class Solution:
2 def getSum(self, nums: List[int]) -> int:
3 MOD = 10**9 + 7
4
5 # Initialize counters for increasing and decreasing sequences
6 increasing_length = 1 # Length of current increasing sequence (diff = 1)
7 decreasing_length = 1 # Length of current decreasing sequence (diff = -1)
8
9 # Initialize cumulative sums for sequences
10 increasing_sum = nums[0] # Cumulative sum for increasing sequence
11 decreasing_sum = nums[0] # Cumulative sum for decreasing sequence
12
13 # Initialize the answer with the first element
14 result = nums[0]
15
16 # Iterate through consecutive pairs of elements
17 for prev_num, curr_num in pairwise(nums):
18 diff = curr_num - prev_num
19
20 # Handle increasing sequence (difference of exactly 1)
21 if diff == 1:
22 increasing_length += 1
23 # Add current number weighted by its position in the sequence
24 increasing_sum += increasing_length * curr_num
25 result = (result + increasing_sum) % MOD
26 else:
27 # Reset increasing sequence
28 increasing_length = 1
29 increasing_sum = curr_num
30
31 # Handle decreasing sequence (difference of exactly -1)
32 if diff == -1:
33 decreasing_length += 1
34 # Add current number weighted by its position in the sequence
35 decreasing_sum += decreasing_length * curr_num
36 result = (result + decreasing_sum) % MOD
37 else:
38 # Reset decreasing sequence
39 decreasing_length = 1
40 decreasing_sum = curr_num
41
42 # Handle non-consecutive elements (neither +1 nor -1 difference)
43 if abs(diff) != 1:
44 result = (result + curr_num) % MOD
45
46 return result
47
1class Solution {
2 public int getSum(int[] nums) {
3 final int MOD = (int) 1e9 + 7;
4
5 // sumIncreasing: cumulative sum for increasing sequences (diff = 1)
6 long sumIncreasing = nums[0];
7 // sumDecreasing: cumulative sum for decreasing sequences (diff = -1)
8 long sumDecreasing = nums[0];
9 // totalSum: accumulator for the final result
10 long totalSum = nums[0];
11
12 // countIncreasing: count of consecutive elements in increasing sequence
13 int countIncreasing = 1;
14 // countDecreasing: count of consecutive elements in decreasing sequence
15 int countDecreasing = 1;
16
17 for (int i = 1; i < nums.length; ++i) {
18 int previousValue = nums[i - 1];
19 int currentValue = nums[i];
20 int difference = currentValue - previousValue;
21
22 // Handle increasing sequence (difference = 1)
23 if (difference == 1) {
24 // Extend the increasing sequence
25 ++countIncreasing;
26 // Add weighted contribution of current value
27 sumIncreasing += (long) countIncreasing * currentValue;
28 totalSum = (totalSum + sumIncreasing) % MOD;
29 } else {
30 // Reset increasing sequence
31 countIncreasing = 1;
32 sumIncreasing = currentValue;
33 }
34
35 // Handle decreasing sequence (difference = -1)
36 if (difference == -1) {
37 // Extend the decreasing sequence
38 ++countDecreasing;
39 // Add weighted contribution of current value
40 sumDecreasing += (long) countDecreasing * currentValue;
41 totalSum = (totalSum + sumDecreasing) % MOD;
42 } else {
43 // Reset decreasing sequence
44 countDecreasing = 1;
45 sumDecreasing = currentValue;
46 }
47
48 // For non-consecutive elements (|difference| != 1), add single value
49 if (Math.abs(difference) != 1) {
50 totalSum = (totalSum + currentValue) % MOD;
51 }
52 }
53
54 return (int) totalSum;
55 }
56}
57
1class Solution {
2public:
3 int getSum(vector<int>& nums) {
4 const int MOD = 1e9 + 7;
5
6 // Variables for tracking increasing sequences (difference of +1)
7 long long increasingSum = nums[0]; // Sum of current increasing sequence
8 int increasingLength = 1; // Length of current increasing sequence
9
10 // Variables for tracking decreasing sequences (difference of -1)
11 long long decreasingSum = nums[0]; // Sum of current decreasing sequence
12 int decreasingLength = 1; // Length of current decreasing sequence
13
14 // Initialize answer with first element
15 long long answer = nums[0];
16
17 // Process each element starting from index 1
18 for (int i = 1; i < nums.size(); ++i) {
19 int previousNum = nums[i - 1];
20 int currentNum = nums[i];
21 int difference = currentNum - previousNum;
22
23 // Handle increasing sequence (difference == 1)
24 if (difference == 1) {
25 // Extend the increasing sequence
26 ++increasingLength;
27 // Add contribution of current number to the sequence sum
28 // Each number contributes based on its position in the sequence
29 increasingSum += 1LL * increasingLength * currentNum;
30 answer = (answer + increasingSum) % MOD;
31 } else {
32 // Reset increasing sequence
33 increasingLength = 1;
34 increasingSum = currentNum;
35 }
36
37 // Handle decreasing sequence (difference == -1)
38 if (difference == -1) {
39 // Extend the decreasing sequence
40 ++decreasingLength;
41 // Add contribution of current number to the sequence sum
42 decreasingSum += 1LL * decreasingLength * currentNum;
43 answer = (answer + decreasingSum) % MOD;
44 } else {
45 // Reset decreasing sequence
46 decreasingLength = 1;
47 decreasingSum = currentNum;
48 }
49
50 // If neither increasing nor decreasing by 1, add the current number once
51 if (abs(difference) != 1) {
52 answer = (answer + currentNum) % MOD;
53 }
54 }
55
56 return answer;
57 }
58};
59
1/**
2 * Calculates the sum of subsequences based on specific difference patterns
3 * @param nums - Array of numbers to process
4 * @returns The sum modulo 10^9 + 7
5 */
6function getSum(nums: number[]): number {
7 const MOD: number = 10 ** 9 + 7;
8
9 // Track consecutive increasing sequences (difference of 1)
10 let increasingLength: number = 1;
11 let increasingSum: number = nums[0];
12
13 // Track consecutive decreasing sequences (difference of -1)
14 let decreasingLength: number = 1;
15 let decreasingSum: number = nums[0];
16
17 // Initialize answer with first element
18 let answer: number = nums[0];
19
20 // Process each element starting from index 1
21 for (let i = 1; i < nums.length; i++) {
22 const previousNum: number = nums[i - 1];
23 const currentNum: number = nums[i];
24 const difference: number = currentNum - previousNum;
25
26 // Handle increasing sequence (difference equals 1)
27 if (difference === 1) {
28 increasingLength++;
29 increasingSum += increasingLength * currentNum;
30 answer = (answer + increasingSum) % MOD;
31 } else {
32 // Reset increasing sequence tracking
33 increasingLength = 1;
34 increasingSum = currentNum;
35 }
36
37 // Handle decreasing sequence (difference equals -1)
38 if (difference === -1) {
39 decreasingLength++;
40 decreasingSum += decreasingLength * currentNum;
41 answer = (answer + decreasingSum) % MOD;
42 } else {
43 // Reset decreasing sequence tracking
44 decreasingLength = 1;
45 decreasingSum = currentNum;
46 }
47
48 // Add current number to answer if difference is not 1 or -1
49 if (Math.abs(difference) !== 1) {
50 answer = (answer + currentNum) % MOD;
51 }
52 }
53
54 return answer;
55}
56
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array nums
. The algorithm iterates through the array once using pairwise(nums)
, which generates consecutive pairs of elements. Since pairwise
yields n-1
pairs for an array of length n
, and each iteration performs a constant number of operations (comparisons, arithmetic operations, and modulo operations), the overall time complexity is linear.
The space complexity is O(1)
. The algorithm uses only a fixed number of variables (mod
, f
, g
, s
, t
, ans
, x
, y
) regardless of the input size. The pairwise
function is a generator that doesn't create additional data structures, it only yields pairs one at a time. Therefore, the space usage remains constant throughout the execution.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Handling of Sequence Resets
The Pitfall: When a sequence breaks (difference is not ±1), developers often forget that the current element still starts a new consecutive subarray of length 1. A common mistake is to only reset the counters without properly accounting for the current element's contribution.
Incorrect Implementation:
if diff == 1: increasing_length += 1 increasing_sum += increasing_length * curr_num result = (result + increasing_sum) % MOD else: # WRONG: Forgetting to add curr_num when it's not part of increasing sequence increasing_length = 1 increasing_sum = 0 # Should be curr_num, not 0!
The Solution: Always initialize the sum with the current element when resetting, as every single element forms a valid consecutive subarray:
else: increasing_length = 1 increasing_sum = curr_num # Correct: current element starts new sequence
2. Double Counting When Difference is Neither +1 nor -1
The Pitfall: The current implementation has a subtle issue where elements are added multiple times when the difference is neither +1 nor -1. The element gets added through both the increasing and decreasing reset operations, plus the explicit non-consecutive check.
Example Problem:
For array [1, 3, 5]
:
- When processing 3 (diff = 2):
- Reset increasing: adds 3 to increasing_sum
- Reset decreasing: adds 3 to decreasing_sum
- Non-consecutive check: adds 3 again
- This leads to overcounting
The Solution: Track whether we've already counted the current element:
for prev_num, curr_num in pairwise(nums): diff = curr_num - prev_num counted = False if diff == 1: increasing_length += 1 increasing_sum += increasing_length * curr_num result = (result + increasing_sum) % MOD counted = True else: increasing_length = 1 increasing_sum = curr_num if diff == -1: decreasing_length += 1 decreasing_sum += decreasing_length * curr_num result = (result + decreasing_sum) % MOD counted = True else: decreasing_length = 1 decreasing_sum = curr_num # Only add if not already counted in a sequence if not counted: result = (result + curr_num) % MOD
3. Overflow Without Proper Modulo Operations
The Pitfall: Forgetting to apply modulo operations during intermediate calculations can cause integer overflow, especially when dealing with large arrays or values.
Incorrect Implementation:
increasing_sum += increasing_length * curr_num # Can overflow result = (result + increasing_sum) % MOD
The Solution: Apply modulo operations to intermediate calculations:
increasing_sum = (increasing_sum + increasing_length * curr_num) % MOD result = (result + increasing_sum) % MOD
4. Missing Edge Case: Single Element Array
The Pitfall:
The pairwise
function won't iterate at all for single-element arrays, but the initialization correctly handles this case. However, if someone modifies the code structure, they might break this handling.
The Solution: Ensure the initialization properly handles single-element arrays and document this behavior:
# Handle single element case through initialization result = nums[0] # This ensures single element arrays return correct result
Given a sorted array of integers and an integer called target, find the element that
equals to the target and return its index. Select the correct code that fills the
___
in the given code snippet.
1def binary_search(arr, target):
2 left, right = 0, len(arr) - 1
3 while left ___ right:
4 mid = (left + right) // 2
5 if arr[mid] == target:
6 return mid
7 if arr[mid] < target:
8 ___ = mid + 1
9 else:
10 ___ = mid - 1
11 return -1
12
1public static int binarySearch(int[] arr, int target) {
2 int left = 0;
3 int right = arr.length - 1;
4
5 while (left ___ right) {
6 int mid = left + (right - left) / 2;
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
16
1function binarySearch(arr, target) {
2 let left = 0;
3 let right = arr.length - 1;
4
5 while (left ___ right) {
6 let mid = left + Math.trunc((right - left) / 2);
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
16
Recommended Readings
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
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
Want a Structured Path to Master System Design Too? Don’t Miss This!