1983. Widest Pair of Indices With Equal Range Sum 🔒
Problem Description
You are given two binary arrays nums1
and nums2
, both 0-indexed (containing only 0s and 1s). Your task is to find a pair of indices (i, j)
where i ≤ j
such that the sum of elements from index i
to index j
in nums1
equals the sum of elements from index i
to index j
in nums2
.
Specifically, you need to find indices where:
nums1[i] + nums1[i+1] + ... + nums1[j] == nums2[i] + nums2[i+1] + ... + nums2[j]
Among all valid pairs of indices, you want to find the widest pair - the one with the largest distance. The distance between a pair of indices (i, j)
is calculated as j - i + 1
.
Your goal is to return the distance of the widest valid pair. If no such pair exists that satisfies the equal sum condition, return 0
.
For example, if you find that indices (2, 5)
form a valid pair where the sums are equal, the distance would be 5 - 2 + 1 = 4
.
The key insight is that this problem can be transformed into finding the longest subarray where the difference between corresponding elements sums to zero. If we create a difference array where each element is nums1[i] - nums2[i]
, we need to find the longest subarray in this difference array that sums to 0
. This transformation allows us to use prefix sum techniques with a hash table to efficiently track positions where the cumulative difference is the same, indicating a zero-sum subarray between those positions.
Intuition
The key observation starts with understanding what we're really looking for. When we want nums1[i] + nums1[i+1] + ... + nums1[j]
to equal nums2[i] + nums2[i+1] + ... + nums2[j]
, we can rearrange this equation by moving all terms to one side:
(nums1[i] - nums2[i]) + (nums1[i+1] - nums2[i+1]) + ... + (nums1[j] - nums2[j]) = 0
This transformation is crucial - instead of comparing two separate sums, we're now looking for a subarray where the differences sum to zero.
Think of it this way: if we create a new array where each element is the difference between corresponding elements of nums1
and nums2
, our problem becomes finding the longest subarray in this difference array that sums to 0
.
Now, how do we find the longest subarray with sum 0
? This is where prefix sums come in. If we have a prefix sum s
at index i
and later encounter the same prefix sum s
at index j
, it means the elements between indices i+1
and j
sum to zero (because the cumulative sum didn't change).
For example, if our prefix sum is 3
at index 2
and again 3
at index 7
, then the subarray from index 3
to 7
must sum to 0
(since adding those elements didn't change our cumulative sum).
To efficiently track where we first saw each prefix sum value, we use a hash table. The hash table stores each unique prefix sum value as a key and its first occurrence index as the value. When we encounter a prefix sum we've seen before, we know we've found a zero-sum subarray, and we can calculate its length.
We initialize the hash table with {0: -1}
because an empty prefix (before index 0) has sum 0
. This handles the edge case where the zero-sum subarray starts from index 0
.
Learn more about Prefix Sum patterns.
Solution Approach
The implementation uses prefix sum combined with a hash table to efficiently find the widest pair of indices.
Step 1: Initialize Data Structures
- Create a hash table
d
with initial value{0: -1}
. This handles the case where a valid subarray starts from index0
. - Initialize
ans = 0
to track the maximum distance found. - Initialize
s = 0
to maintain the running prefix sum of differences.
Step 2: Process Arrays Element by Element
We iterate through both arrays simultaneously using enumerate(zip(nums1, nums2))
:
- For each index
i
and corresponding elementsa
fromnums1
andb
fromnums2
:- Update the prefix sum:
s += a - b
- This accumulates the difference between corresponding elements
- Update the prefix sum:
Step 3: Check for Valid Subarrays
For each position i
with prefix sum s
:
- If
s
exists in hash tabled
:- This means we've seen this prefix sum before at index
d[s]
- The subarray from index
d[s] + 1
toi
has sum0
- Calculate the distance:
i - d[s]
- Update the answer:
ans = max(ans, i - d[s])
- This means we've seen this prefix sum before at index
- If
s
is not in hash tabled
:- Record this as the first occurrence of prefix sum
s
- Store
d[s] = i
- Record this as the first occurrence of prefix sum
Why This Works: When we find the same prefix sum at two different indices, it means the elements between those indices sum to zero. For example:
- At index
j
: prefix sum =5
- At index
k
: prefix sum =5
- This means elements from
j+1
tok
sum to0
(since the cumulative sum didn't change)
Time and Space Complexity:
- Time:
O(n)
wheren
is the length of the arrays - we traverse once through both arrays - Space:
O(n)
in worst case for the hash table if all prefix sums are unique
The algorithm efficiently finds the widest valid pair by keeping track of only the first occurrence of each prefix sum, ensuring we always get the maximum possible distance when we encounter a repeated prefix sum.
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 concrete example to illustrate the solution approach.
Given:
nums1 = [1, 1, 0, 1, 0, 1]
nums2 = [0, 1, 1, 0, 1, 1]
Step 1: Create the difference array
For each index, calculate nums1[i] - nums2[i]
:
- Index 0:
1 - 0 = 1
- Index 1:
1 - 1 = 0
- Index 2:
0 - 1 = -1
- Index 3:
1 - 0 = 1
- Index 4:
0 - 1 = -1
- Index 5:
1 - 1 = 0
Difference array: [1, 0, -1, 1, -1, 0]
Step 2: Process with prefix sum and hash table
Initialize:
- Hash table
d = {0: -1}
(represents empty prefix before index 0) ans = 0
(maximum distance found)s = 0
(running prefix sum)
Iteration process:
Index | Element Diff | Prefix Sum s | Hash Table d | Action | Distance Calculation |
---|---|---|---|---|---|
0 | 1 | 1 | {0: -1, 1: 0} | Store first occurrence of sum 1 | - |
1 | 0 | 1 | {0: -1, 1: 0} | Sum 1 seen before at index 0 | 1 - 0 = 1 |
2 | -1 | 0 | {0: -1, 1: 0} | Sum 0 seen before at index -1 | 2 - (-1) = 3 |
3 | 1 | 1 | {0: -1, 1: 0} | Sum 1 seen before at index 0 | 3 - 0 = 3 |
4 | -1 | 0 | {0: -1, 1: 0} | Sum 0 seen before at index -1 | 4 - (-1) = 5 |
5 | 0 | 0 | {0: -1, 1: 0} | Sum 0 seen before at index -1 | 5 - (-1) = 6 |
Step 3: Find the maximum distance The maximum distance found is 6, which corresponds to the entire array from index 0 to 5.
Verification: Let's verify that indices (0, 5) give equal sums:
nums1[0] + nums1[1] + nums1[2] + nums1[3] + nums1[4] + nums1[5] = 1 + 1 + 0 + 1 + 0 + 1 = 4
nums2[0] + nums2[1] + nums2[2] + nums2[3] + nums2[4] + nums2[5] = 0 + 1 + 1 + 0 + 1 + 1 = 4
The sums are equal (both are 4), confirming our answer. The distance is 5 - 0 + 1 = 6
.
Key Insights from the Example:
- When the prefix sum returns to a previously seen value, it indicates a zero-sum subarray in the difference array
- By storing only the first occurrence of each prefix sum, we maximize the distance when we encounter it again
- The special initialization
{0: -1}
allows us to handle subarrays that start from index 0
Solution Implementation
1class Solution:
2 def widestPairOfIndices(self, nums1: List[int], nums2: List[int]) -> int:
3 # Dictionary to store the first occurrence index of each prefix sum difference
4 # Initialize with 0: -1 to handle subarrays starting from index 0
5 first_occurrence = {0: -1}
6
7 # Track the maximum width and running sum difference
8 max_width = 0
9 sum_difference = 0
10
11 # Iterate through both arrays simultaneously
12 for index, (value1, value2) in enumerate(zip(nums1, nums2)):
13 # Calculate cumulative difference between corresponding elements
14 sum_difference += value1 - value2
15
16 # If this sum difference was seen before, we found a valid subarray
17 # where sum(nums1[left+1:right+1]) - sum(nums2[left+1:right+1]) = 0
18 if sum_difference in first_occurrence:
19 # Update maximum width if current subarray is wider
20 max_width = max(max_width, index - first_occurrence[sum_difference])
21 else:
22 # Record the first occurrence of this sum difference
23 first_occurrence[sum_difference] = index
24
25 return max_width
26
1class Solution {
2 public int widestPairOfIndices(int[] nums1, int[] nums2) {
3 // HashMap to store the first occurrence index of each prefix sum difference
4 // Key: prefix sum difference, Value: index where this difference first occurred
5 Map<Integer, Integer> firstOccurrenceMap = new HashMap<>();
6
7 // Initialize with difference 0 at index -1 (before array starts)
8 // This handles cases where valid subarray starts from index 0
9 firstOccurrenceMap.put(0, -1);
10
11 int arrayLength = nums1.length;
12 int prefixSumDifference = 0; // Running difference between prefix sums
13 int maxWidth = 0; // Maximum width found so far
14
15 // Iterate through both arrays simultaneously
16 for (int currentIndex = 0; currentIndex < arrayLength; ++currentIndex) {
17 // Update the prefix sum difference
18 // If sum(nums1[i..j]) == sum(nums2[i..j]), then
19 // prefixSum1[j] - prefixSum1[i-1] == prefixSum2[j] - prefixSum2[i-1]
20 // Which means prefixSumDiff[j] == prefixSumDiff[i-1]
21 prefixSumDifference += nums1[currentIndex] - nums2[currentIndex];
22
23 // Check if we've seen this prefix sum difference before
24 if (firstOccurrenceMap.containsKey(prefixSumDifference)) {
25 // Found a valid subarray from (firstOccurrence + 1) to currentIndex
26 // Update maximum width if this subarray is wider
27 int width = currentIndex - firstOccurrenceMap.get(prefixSumDifference);
28 maxWidth = Math.max(maxWidth, width);
29 } else {
30 // First time seeing this difference, record its position
31 firstOccurrenceMap.put(prefixSumDifference, currentIndex);
32 }
33 }
34
35 return maxWidth;
36 }
37}
38
1class Solution {
2public:
3 int widestPairOfIndices(vector<int>& nums1, vector<int>& nums2) {
4 // HashMap to store the first occurrence index of each prefix difference
5 // Key: prefix sum difference (nums1[0..i] - nums2[0..i])
6 // Value: the earliest index where this difference occurs
7 unordered_map<int, int> firstOccurrenceMap;
8
9 // Initialize with difference 0 at index -1 (before array starts)
10 // This handles the case where a valid subarray starts from index 0
11 firstOccurrenceMap[0] = -1;
12
13 int maxWidth = 0; // Maximum width found so far
14 int prefixDifference = 0; // Running difference between prefix sums
15 int arraySize = nums1.size();
16
17 // Iterate through both arrays simultaneously
18 for (int currentIndex = 0; currentIndex < arraySize; ++currentIndex) {
19 // Update the running difference: sum(nums1[0..i]) - sum(nums2[0..i])
20 prefixDifference += nums1[currentIndex] - nums2[currentIndex];
21
22 // Check if we've seen this prefix difference before
23 if (firstOccurrenceMap.count(prefixDifference)) {
24 // If yes, we found a subarray where sum(nums1) == sum(nums2)
25 // The subarray spans from (firstOccurrenceMap[prefixDifference] + 1) to currentIndex
26 int currentWidth = currentIndex - firstOccurrenceMap[prefixDifference];
27 maxWidth = max(maxWidth, currentWidth);
28 } else {
29 // First time seeing this difference, record its position
30 firstOccurrenceMap[prefixDifference] = currentIndex;
31 }
32 }
33
34 return maxWidth;
35 }
36};
37
1/**
2 * Finds the widest pair of indices where the sum of elements from nums1
3 * equals the sum of elements from nums2 in the subarray
4 * @param nums1 - First array of numbers
5 * @param nums2 - Second array of numbers
6 * @returns The maximum width between valid index pairs
7 */
8function widestPairOfIndices(nums1: number[], nums2: number[]): number {
9 // Map to store the first occurrence index of each prefix difference
10 const prefixDifferenceMap: Map<number, number> = new Map();
11
12 // Initialize with difference 0 at index -1 (before array starts)
13 prefixDifferenceMap.set(0, -1);
14
15 const arrayLength: number = nums1.length;
16 let currentPrefixDifference: number = 0;
17 let maxWidth: number = 0;
18
19 // Iterate through both arrays simultaneously
20 for (let currentIndex = 0; currentIndex < arrayLength; ++currentIndex) {
21 // Calculate cumulative difference between nums1 and nums2
22 currentPrefixDifference += nums1[currentIndex] - nums2[currentIndex];
23
24 // If we've seen this difference before, we found a valid subarray
25 if (prefixDifferenceMap.has(currentPrefixDifference)) {
26 // Calculate width and update maximum if larger
27 const previousIndex: number = prefixDifferenceMap.get(currentPrefixDifference)!;
28 maxWidth = Math.max(maxWidth, currentIndex - previousIndex);
29 } else {
30 // First occurrence of this difference, store the index
31 prefixDifferenceMap.set(currentPrefixDifference, currentIndex);
32 }
33 }
34
35 return maxWidth;
36}
37
Time and Space Complexity
The time complexity is O(n)
where n
is the length of the arrays nums1
and nums2
. The algorithm iterates through both arrays exactly once using the enumerate(zip(nums1, nums2))
construct, performing constant-time operations for each element: calculating the difference a - b
, checking dictionary membership, updating the maximum, and potentially adding to the dictionary.
The space complexity is O(n)
where n
is the length of the arrays. In the worst case, when all cumulative sums s
are unique, the dictionary d
will store n + 1
entries (including the initial entry {0: -1}
plus one entry for each iteration). Each dictionary entry uses constant space to store the sum as a key and the index as a value.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Forgetting to Initialize the Hash Table with {0: -1}
One of the most common mistakes is initializing an empty hash table instead of starting with {0: -1}
. This initialization is crucial for handling subarrays that start from index 0.
Why it matters: If the cumulative difference becomes 0 at some index i
, it means the entire subarray from index 0 to i
has equal sums in both arrays. Without the initial {0: -1}
, we would miss these valid subarrays.
Example scenario:
nums1 = [1, 0, 1]
nums2 = [0, 1, 1]
- At index 2, the cumulative difference is 0 (meaning nums1[0:3] sum equals nums2[0:3] sum)
- Without
{0: -1}
, we'd miss this valid subarray of length 3
Solution: Always initialize with first_occurrence = {0: -1}
rather than first_occurrence = {}
2. Updating the Hash Table for Every Occurrence of a Prefix Sum
Another mistake is updating the hash table every time we see a prefix sum, not just the first time.
Why it matters: We want the maximum width, so we should keep the earliest (leftmost) occurrence of each prefix sum. Updating it with later occurrences would give us smaller widths.
Incorrect approach:
# WRONG: This updates every time, losing track of the earliest occurrence
for index, (value1, value2) in enumerate(zip(nums1, nums2)):
sum_difference += value1 - value2
if sum_difference in first_occurrence:
max_width = max(max_width, index - first_occurrence[sum_difference])
first_occurrence[sum_difference] = index # Updates regardless!
Correct approach:
# RIGHT: Only store the first occurrence
for index, (value1, value2) in enumerate(zip(nums1, nums2)):
sum_difference += value1 - value2
if sum_difference in first_occurrence:
max_width = max(max_width, index - first_occurrence[sum_difference])
else:
first_occurrence[sum_difference] = index # Only on first occurrence
3. Misunderstanding the Distance Calculation
Some developers might incorrectly calculate the distance as i - d[s]
instead of understanding why this is correct, leading to off-by-one errors when trying to "fix" it.
Why the formula works: When we have the same prefix sum at indices j
and i
, the valid subarray spans from j+1
to i
(inclusive). The number of elements in this range is i - j
, which equals i - d[s]
when d[s] = j
.
Common mistake: Adding 1 to the formula thinking it needs adjustment: i - d[s] + 1
(incorrect)
Solution: Trust the formula i - d[s]
as it correctly calculates the width of the subarray from index d[s] + 1
to i
.
Problem: Given a list of tasks and a list of requirements, compute a sequence of tasks that can be performed, such that we complete every task once while satisfying all the requirements.
Which of the following method should we use to solve this problem?
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!