LeetCode 1477 - Find Two Non-overlapping Sub-arrays Each With Target Sum
The problem asks us to find two different subarrays inside the given array such that: 1. Each subarray has a sum exactly
Difficulty: 🟡 Medium
Topics: Array, Hash Table, Binary Search, Dynamic Programming, Sliding Window
Solution
Problem Understanding
The problem asks us to find two different subarrays inside the given array such that:
- Each subarray has a sum exactly equal to
target - The two subarrays do not overlap
- The total length of the two chosen subarrays is as small as possible
A subarray is a contiguous portion of the array. Since the subarrays must not overlap, if one subarray uses indices [l1, r1] and another uses [l2, r2], then either r1 < l2 or r2 < l1.
The input consists of:
arr, an array of positive integerstarget, the desired sum for each subarray
The output is:
- The minimum possible sum of lengths of two non-overlapping subarrays whose sums equal
target -1if no such pair exists
The constraints are very important:
arr.lengthcan be as large as10^5- Each element is positive
targetcan also be large
Because the array size is large, any algorithm slower than roughly O(n log n) or O(n) is likely too slow. A naive solution that checks every possible pair of subarrays would be far too expensive.
An important property is that all numbers are positive. This allows us to use a sliding window efficiently, because increasing the window always increases the sum, and shrinking the window always decreases it.
Several edge cases are important:
- The array may contain only one valid subarray, in which case the answer is
-1 - Multiple valid subarrays may overlap, but overlapping pairs are not allowed
- The optimal answer may involve two very small subarrays far apart in the array
- There may be many valid subarrays, requiring us to carefully track the minimum length seen so far
Approaches
Brute Force Approach
A straightforward solution is to generate every subarray whose sum equals target, then compare every pair of such subarrays to find the minimum combined length among non-overlapping pairs.
We can do this in several steps:
- Enumerate all subarrays
- Store every subarray whose sum equals
target - Compare every pair
- Check whether they overlap
- Track the minimum combined length
This approach is correct because it explicitly checks every valid possibility.
However, it is too slow.
There are O(n^2) possible subarrays. Comparing all pairs of valid subarrays can also take O(n^2) time in the worst case. This can lead to overall complexity approaching O(n^4) in the naive version, or O(n^2) even with optimizations.
With n = 100000, this is completely infeasible.
Optimal Approach
The key insight is that we do not need to compare every pair explicitly.
Instead, while scanning the array from left to right, we can:
- Use a sliding window to find subarrays with sum equal to
target - Keep track of the shortest valid subarray ending at or before each position
- Whenever we find a new valid subarray, combine it with the best earlier non-overlapping subarray
Because all numbers are positive, the sliding window works efficiently in linear time.
We maintain:
best[i], the minimum length of a valid subarray ending at or before indexi- A running answer representing the best pair found so far
When we discover a subarray [left, right]:
- Its length is
right - left + 1 - Any previous valid subarray must end before
left - So we combine the current length with
best[left - 1]
This avoids comparing every pair directly.
Approach Comparison
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n²) to O(n⁴) | O(n²) | Enumerates and compares many subarrays |
| Optimal | O(n) | O(n) | Sliding window with DP tracking minimum lengths |
Algorithm Walkthrough
Step 1: Initialize Variables
We use:
leftfor the sliding window startcurrent_sumfor the current window sumbestarray wherebest[i]stores the shortest valid subarray ending at or before indexianswerinitialized to infinity
The best array is critical because it lets us instantly retrieve the shortest earlier valid subarray.
Step 2: Expand the Sliding Window
Iterate through the array using right.
At each step:
- Add
arr[right]tocurrent_sum
Since all values are positive, the window sum only increases when we expand.
Step 3: Shrink the Window When Needed
If current_sum > target, shrink the window from the left:
- Subtract
arr[left] - Increment
left
We continue shrinking until current_sum <= target.
Because numbers are positive, once the sum exceeds the target, moving left forward is the only way to reduce it.
Step 4: Detect Valid Subarrays
Whenever current_sum == target, we found a valid subarray [left, right].
Compute:
length = right - left + 1
Step 5: Combine With Previous Non-overlapping Subarrays
To avoid overlap:
- Any earlier subarray must end before
left
If left > 0 and best[left - 1] is valid, then:
answer = min(answer, length + best[left - 1])
This combines the current subarray with the best earlier compatible one.
Step 6: Update the Best Array
We update:
best[right] = min(best[right - 1], length)
This means:
- Either the best previous solution remains optimal
- Or the current subarray is shorter
If no valid subarray ends at right, then:
best[right] = best[right - 1]
Step 7: Return the Result
If answer is still infinity, return -1.
Otherwise return answer.
Why it works
The algorithm works because at every index we maintain the shortest valid subarray ending at or before that position. When a new valid subarray is found, we immediately combine it with the best earlier non-overlapping subarray. Since every valid subarray is processed exactly once and every compatible earlier subarray is summarized inside best, the algorithm never misses the optimal pair.
Python Solution
from typing import List
class Solution:
def minSumOfLengths(self, arr: List[int], target: int) -> int:
n = len(arr)
INF = float('inf')
best = [INF] * n
left = 0
current_sum = 0
answer = INF
for right in range(n):
current_sum += arr[right]
while current_sum > target:
current_sum -= arr[left]
left += 1
if current_sum == target:
current_length = right - left + 1
if left > 0 and best[left - 1] != INF:
answer = min(
answer,
current_length + best[left - 1]
)
best[right] = current_length
if right > 0:
best[right] = min(best[right], best[right - 1])
return -1 if answer == INF else answer
The implementation follows the algorithm directly.
The best array stores the minimum valid subarray length seen so far up to each position. Initially, every entry is infinity because no valid subarray has been found.
The sliding window is maintained using left and right. Since all elements are positive, the window can be adjusted efficiently without revisiting elements excessively.
Whenever a valid subarray is discovered, we first compute its length. Then we check whether there exists a non-overlapping earlier subarray using best[left - 1].
Finally, we propagate the minimum value forward inside the best array so every position contains the shortest valid subarray ending at or before that index.
Go Solution
package main
import "math"
func minSumOfLengths(arr []int, target int) int {
n := len(arr)
const INF = math.MaxInt32
best := make([]int, n)
for i := 0; i < n; i++ {
best[i] = INF
}
left := 0
currentSum := 0
answer := INF
for right := 0; right < n; right++ {
currentSum += arr[right]
for currentSum > target {
currentSum -= arr[left]
left++
}
if currentSum == target {
currentLength := right - left + 1
if left > 0 && best[left-1] != INF {
if currentLength+best[left-1] < answer {
answer = currentLength + best[left-1]
}
}
best[right] = currentLength
}
if right > 0 && best[right-1] < best[right] {
best[right] = best[right-1]
}
}
if answer == INF {
return -1
}
return answer
}
The Go implementation mirrors the Python logic closely.
Go does not have a built-in infinity value for integers, so math.MaxInt32 is used as a sentinel value.
Slices are initialized explicitly, and minimum comparisons are written manually because Go does not provide a built-in min function for integers in older versions.
Since the constraints fit comfortably inside 32-bit integers, integer overflow is not a concern here.
Worked Examples
Example 1
arr = [3,2,2,4,3]
target = 3
Valid subarrays:
[3]at index 0[3]at index 4
| right | Window | Sum | Valid? | best[right] | answer |
|---|---|---|---|---|---|
| 0 | [3] | 3 | Yes | 1 | INF |
| 1 | [2] | 2 | No | 1 | INF |
| 2 | [2,2] | 4 | Shrink | 1 | INF |
| 3 | [2,4] | 6 | Shrink | 1 | INF |
| 4 | [3] | 3 | Yes | 1 | 2 |
At index 4:
- Current subarray length = 1
best[3] = 1- Total =
1 + 1 = 2
Final answer:
2
Example 2
arr = [7,3,4,7]
target = 7
Valid subarrays:
[7]at index 0[3,4][7]at index 3
| right | Valid Subarray | Length | best[right] | answer |
|---|---|---|---|---|
| 0 | [0,0] | 1 | 1 | INF |
| 2 | [1,2] | 2 | 1 | 3 |
| 3 | [3,3] | 1 | 1 | 2 |
The best pair is:
- First
[7] - Last
[7]
Total length:
1 + 1 = 2
Example 3
arr = [4,3,2,6,2,3,4]
target = 6
Only one valid subarray exists:
[6]
Since two non-overlapping subarrays are required, the result is:
-1
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Each element enters and leaves the sliding window at most once |
| Space | O(n) | The best array stores one value per index |
The sliding window guarantees linear processing because both pointers only move forward. No nested rescanning occurs. The best array requires linear additional memory.
Test Cases
from typing import List
class Solution:
def minSumOfLengths(self, arr: List[int], target: int) -> int:
n = len(arr)
INF = float('inf')
best = [INF] * n
left = 0
current_sum = 0
answer = INF
for right in range(n):
current_sum += arr[right]
while current_sum > target:
current_sum -= arr[left]
left += 1
if current_sum == target:
current_length = right - left + 1
if left > 0 and best[left - 1] != INF:
answer = min(
answer,
current_length + best[left - 1]
)
best[right] = current_length
if right > 0:
best[right] = min(best[right], best[right - 1])
return -1 if answer == INF else answer
sol = Solution()
assert sol.minSumOfLengths([3,2,2,4,3], 3) == 2
# Basic example with two single-element subarrays
assert sol.minSumOfLengths([7,3,4,7], 7) == 2
# Best solution uses two isolated single-element windows
assert sol.minSumOfLengths([4,3,2,6,2,3,4], 6) == -1
# Only one valid subarray exists
assert sol.minSumOfLengths([1,1,1,1,1], 2) == 4
# Multiple overlapping windows, choose non-overlapping ones
assert sol.minSumOfLengths([5,5,4,4,5], 3) == -1
# No valid subarray exists
assert sol.minSumOfLengths([3,1,1,1,5,1,2,1], 3) == 3
# Combination of different-sized windows
assert sol.minSumOfLengths([1,6,1], 7) == -1
# Entire array forms one valid subarray only
assert sol.minSumOfLengths([2,1,1,2,1,1,2], 3) == 4
# Multiple equal-length valid windows
assert sol.minSumOfLengths([1,2,2,3,2,2,1], 4) == 4
# Non-overlapping middle windows
assert sol.minSumOfLengths([1,1,1,2,2,2,1,1], 3) == 4
# Many overlapping possibilities
Test Summary
| Test | Why |
|---|---|
[3,2,2,4,3], 3 |
Basic example from prompt |
[7,3,4,7], 7 |
Multiple valid choices |
[4,3,2,6,2,3,4], 6 |
Only one valid subarray |
[1,1,1,1,1], 2 |
Heavy overlap between windows |
[5,5,4,4,5], 3 |
No solution exists |
[3,1,1,1,5,1,2,1], 3 |
Mixed subarray lengths |
[1,6,1], 7 |
Single valid full-array window |
[2,1,1,2,1,1,2], 3 |
Multiple equal candidates |
[1,2,2,3,2,2,1], 4 |
Interior optimal windows |
[1,1,1,2,2,2,1,1], 3 |
Many overlapping combinations |
Edge Cases
One important edge case occurs when only one valid subarray exists. A naive implementation might accidentally return its length instead of recognizing that two non-overlapping subarrays are required. This implementation avoids that mistake because the answer is only updated when a previous valid subarray already exists in the best array.
Another important edge case involves overlapping subarrays. For example, in [1,1,1,1] with target 2, many valid windows overlap. The algorithm carefully checks best[left - 1], ensuring that the earlier subarray ends strictly before the current one begins.
A third edge case occurs when the optimal solution uses very small subarrays far apart in the array. A greedy approach that always chooses the first valid subarray could fail here. The best array solves this by continuously tracking the shortest valid subarray seen so far, guaranteeing that every future window is paired with the globally optimal earlier choice.
A final edge case involves arrays with no valid subarrays at all. In this situation, the answer variable remains infinity throughout execution, and the algorithm correctly returns -1.