2364. Count Number of Bad Pairs
Problem Description
You are given a 0-indexed integer array nums
. A pair of indices (i, j)
is called a bad pair if it satisfies both conditions:
i < j
j - i != nums[j] - nums[i]
Your task is to return the total number of bad pairs in the array nums
.
To understand this problem better, let's break down what makes a pair "bad":
- We're looking at pairs of indices where the first index comes before the second (
i < j
) - The difference between the indices (
j - i
) should NOT equal the difference between their corresponding values (nums[j] - nums[i]
) - If these differences are not equal, we count it as a bad pair
For example, if we have indices i = 1
and j = 3
with values nums[1] = 5
and nums[3] = 8
:
- Index difference:
j - i = 3 - 1 = 2
- Value difference:
nums[j] - nums[i] = 8 - 5 = 3
- Since
2 ≠ 3
, this would be a bad pair
The solution cleverly transforms the condition j - i != nums[j] - nums[i]
into i - nums[i] != j - nums[j]
. This transformation allows us to use a hash table to efficiently count pairs. For each position, we track how many previous positions have the same value of index - nums[index]
. The positions that don't match this pattern with the current position form bad pairs.
Intuition
The key insight comes from recognizing that counting "bad pairs" directly would be inefficient - we'd need to check every possible pair, resulting in O(n²) time complexity. Instead, we can count the "good pairs" and subtract from the total number of pairs.
Let's think about what makes a pair "good". A pair (i, j)
where i < j
is good when j - i = nums[j] - nums[i]
. We can rearrange this equation:
j - i = nums[j] - nums[i]
j - nums[j] = i - nums[i]
This transformation is brilliant! It tells us that for a good pair, both indices have the same value when we compute index - nums[index]
.
Now, instead of checking pairs, we can track a pattern: for each position i
, we calculate i - nums[i]
. If we've seen this value before at some earlier positions, those positions form good pairs with the current position.
Here's the counting logic:
- When we're at position
i
, all positions before it (from0
toi-1
) could potentially form pairs with it - That's a total of
i
possible pairs - Among these, the good pairs are those previous positions where
index - nums[index]
equalsi - nums[i]
- So the bad pairs with position
i
are:i - (count of good pairs)
By maintaining a hash table that counts occurrences of i - nums[i]
as we iterate through the array, we can efficiently determine how many good pairs exist for each position, and consequently, how many bad pairs there are.
Learn more about Math patterns.
Solution Approach
Based on our intuition, we implement the solution using equation transformation and a hash table. The core idea is to transform the bad pair condition and use a counter to efficiently track patterns.
Equation Transformation:
We start with the bad pair condition: j - i != nums[j] - nums[i]
for i < j
.
By rearranging, we get: i - nums[i] != j - nums[j]
.
This means a pair (i, j)
is bad when the values i - nums[i]
and j - nums[j]
are different.
Implementation Steps:
-
Initialize a Counter: We use a hash table
cnt
to count occurrences ofi - nums[i]
values as we iterate through the array. -
Iterate Through the Array: For each position
i
with valuenums[i]
:- Calculate the key:
key = i - nums[i]
- Count bad pairs: All previous positions (total of
i
positions) minus the positions that have the same key value gives us the number of bad pairs ending at positioni
- Add to answer:
ans += i - cnt[key]
- Update counter: Increment
cnt[key]
by 1 for future iterations
- Calculate the key:
-
Return the Result: The accumulated sum gives us the total number of bad pairs.
Example Walkthrough:
Let's say nums = [4, 1, 3, 3]
:
- Position 0:
key = 0 - 4 = -4
, bad pairs =0 - 0 = 0
, updatecnt[-4] = 1
- Position 1:
key = 1 - 1 = 0
, bad pairs =1 - 0 = 1
, updatecnt[0] = 1
- Position 2:
key = 2 - 3 = -1
, bad pairs =2 - 0 = 2
, updatecnt[-1] = 1
- Position 3:
key = 3 - 3 = 0
, bad pairs =3 - 1 = 2
(one good pair with position 1), updatecnt[0] = 2
- Total bad pairs =
0 + 1 + 2 + 2 = 5
Time and Space Complexity:
- Time: O(n) - single pass through the array
- Space: O(n) - hash table can store at most n different keys
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 small example with nums = [1, 2, 5]
to illustrate the solution approach.
Step-by-step process:
We'll track i - nums[i]
for each position and use a counter to find bad pairs.
Initial state:
cnt = {}
(empty hash table)ans = 0
(bad pairs count)
Position 0 (nums[0] = 1):
- Calculate key:
0 - 1 = -1
- Previous positions that could form pairs: 0 (none before this)
- Good pairs with same key (-1):
cnt[-1] = 0
(not in counter yet) - Bad pairs:
0 - 0 = 0
- Update:
ans = 0
,cnt[-1] = 1
Position 1 (nums[1] = 2):
- Calculate key:
1 - 2 = -1
- Previous positions that could form pairs: 1 (positions 0)
- Good pairs with same key (-1):
cnt[-1] = 1
(position 0 has key -1) - Bad pairs:
1 - 1 = 0
- Update:
ans = 0
,cnt[-1] = 2
Position 2 (nums[2] = 5):
- Calculate key:
2 - 5 = -3
- Previous positions that could form pairs: 2 (positions 0 and 1)
- Good pairs with same key (-3):
cnt[-3] = 0
(not in counter) - Bad pairs:
2 - 0 = 2
- Update:
ans = 2
,cnt[-3] = 1
Verification: Let's verify our answer by checking all pairs manually:
- Pair (0,1):
i=0, j=1
→j-i = 1
,nums[j]-nums[i] = 2-1 = 1
→1 = 1
(good pair) - Pair (0,2):
i=0, j=2
→j-i = 2
,nums[j]-nums[i] = 5-1 = 4
→2 ≠ 4
(bad pair) - Pair (1,2):
i=1, j=2
→j-i = 1
,nums[j]-nums[i] = 5-2 = 3
→1 ≠ 3
(bad pair)
Result: 2 bad pairs total, which matches our algorithm's output.
The key insight is that positions 0 and 1 both have i - nums[i] = -1
, making them a good pair. Position 2 has a different value (-3), so it forms bad pairs with both previous positions.
Solution Implementation
1from typing import List
2from collections import Counter
3
4class Solution:
5 def countBadPairs(self, nums: List[int]) -> int:
6 # Dictionary to count occurrences of (index - value) differences
7 difference_count = Counter()
8 # Total number of bad pairs
9 bad_pairs = 0
10
11 for index, value in enumerate(nums):
12 # Calculate the difference for current position
13 current_difference = index - value
14
15 # Count bad pairs: all previous indices minus good pairs
16 # Good pairs are those with same difference value
17 good_pairs_with_current = difference_count[current_difference]
18 bad_pairs_with_current = index - good_pairs_with_current
19 bad_pairs += bad_pairs_with_current
20
21 # Update the counter for future iterations
22 difference_count[current_difference] += 1
23
24 return bad_pairs
25
1class Solution {
2 public long countBadPairs(int[] nums) {
3 // Map to store frequency of (index - value) differences
4 // Key: difference value (i - nums[i])
5 // Value: count of indices with this difference
6 Map<Integer, Integer> frequencyMap = new HashMap<>();
7
8 long badPairCount = 0;
9
10 // Iterate through each element in the array
11 for (int currentIndex = 0; currentIndex < nums.length; ++currentIndex) {
12 // Calculate the difference for current position
13 int difference = currentIndex - nums[currentIndex];
14
15 // Get current frequency of this difference (default 0)
16 int currentFrequency = frequencyMap.getOrDefault(difference, 0);
17
18 // Update frequency map with incremented count
19 frequencyMap.put(difference, currentFrequency + 1);
20
21 // Calculate bad pairs for current index
22 // Total pairs with current index: currentIndex
23 // Good pairs (same difference): currentFrequency
24 // Bad pairs: currentIndex - currentFrequency
25 badPairCount += currentIndex - currentFrequency;
26 }
27
28 return badPairCount;
29 }
30}
31
1class Solution {
2public:
3 long long countBadPairs(vector<int>& nums) {
4 // Map to store frequency of (index - value) differences
5 // Key: difference value (i - nums[i])
6 // Value: count of indices with this difference
7 unordered_map<int, int> differenceCount;
8
9 // Total count of bad pairs
10 long long badPairCount = 0;
11
12 // Iterate through each element in the array
13 for (int i = 0; i < nums.size(); ++i) {
14 // Calculate the difference for current index
15 // This represents the characteristic value (i - nums[i])
16 int currentDifference = i - nums[i];
17
18 // Count bad pairs with current element as the second element
19 // All previous indices except those with same difference form bad pairs
20 // i: total previous elements
21 // differenceCount[currentDifference]: previous elements with same difference (good pairs)
22 // Bad pairs = total previous - good pairs
23 badPairCount += i - differenceCount[currentDifference];
24
25 // Update the frequency count for current difference
26 // Post-increment returns old value before incrementing
27 differenceCount[currentDifference]++;
28 }
29
30 return badPairCount;
31 }
32};
33
1/**
2 * Counts the number of bad pairs in the array.
3 * A bad pair (i, j) is defined where i < j and j - i != nums[j] - nums[i]
4 * This can be rewritten as: i - nums[i] != j - nums[j]
5 *
6 * @param nums - The input array of numbers
7 * @returns The total count of bad pairs
8 */
9function countBadPairs(nums: number[]): number {
10 // Map to store frequency of (index - value) differences
11 // Key: difference value (i - nums[i]), Value: count of occurrences
12 const differenceFrequencyMap = new Map<number, number>();
13
14 // Initialize the count of bad pairs
15 let badPairCount = 0;
16
17 // Iterate through each element in the array
18 for (let currentIndex = 0; currentIndex < nums.length; ++currentIndex) {
19 // Calculate the difference for current position: index - value
20 const currentDifference = currentIndex - nums[currentIndex];
21
22 // Count bad pairs: all previous indices minus good pairs with same difference
23 // Good pairs have the same difference value (i - nums[i] == j - nums[j])
24 const goodPairsWithCurrentIndex = differenceFrequencyMap.get(currentDifference) ?? 0;
25 badPairCount += currentIndex - goodPairsWithCurrentIndex;
26
27 // Update the frequency map with current difference
28 differenceFrequencyMap.set(
29 currentDifference,
30 (differenceFrequencyMap.get(currentDifference) ?? 0) + 1
31 );
32 }
33
34 return badPairCount;
35}
36
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array nums
. This is because the algorithm iterates through the array exactly once using enumerate(nums)
, and within each iteration, it performs constant-time operations: accessing and updating the Counter
dictionary (both cnt[i - x]
and cnt[i - x] += 1
are O(1)
operations on average), and performing arithmetic operations (i - cnt[i - x]
).
The space complexity is O(n)
, where n
is the length of the array nums
. This is due to the Counter
dictionary cnt
which stores key-value pairs. In the worst case, when all values of i - nums[i]
are unique for each index i
, the dictionary will contain n
distinct keys, requiring O(n)
space to store them.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Integer Overflow in Other Languages
While Python handles large integers automatically, in languages like Java or C++, the calculation of bad pairs can cause integer overflow when dealing with large arrays.
Problem Example:
- Array size n = 100,000
- Worst case: all pairs are bad = n*(n-1)/2 ≈ 5 billion (exceeds 32-bit integer range)
Solution: Use 64-bit integers (long in Java, long long in C++) for the answer variable:
long badPairs = 0; // Use long instead of int
2. Misunderstanding the Good vs Bad Pair Logic
A common mistake is incorrectly calculating the number of bad pairs at each position. Some might accidentally count good pairs as bad pairs or vice versa.
Problem Example:
# WRONG: This counts good pairs, not bad pairs bad_pairs += difference_count[current_difference]
Solution: Remember that at position i:
- Total possible pairs with previous elements = i
- Good pairs = difference_count[current_difference]
- Bad pairs = Total - Good = i - difference_count[current_difference]
3. Forgetting to Update the Counter
Failing to update the difference counter after processing each element will lead to incorrect counts for subsequent elements.
Problem Example:
for index, value in enumerate(nums):
current_difference = index - value
bad_pairs += index - difference_count[current_difference]
# MISSING: difference_count[current_difference] += 1
Solution: Always update the counter after calculating bad pairs for the current position:
difference_count[current_difference] += 1
4. Using Wrong Difference Formula
Some might use value - index
instead of index - value
, which would give incorrect results.
Problem Example:
# WRONG: This reverses the difference current_difference = value - index # Should be index - value
Solution:
Stick to the transformed equation: use index - nums[index]
consistently throughout the solution.
5. Not Handling Edge Cases
While this solution handles them implicitly, not considering edge cases can lead to errors in modified versions.
Edge Cases to Consider:
- Empty array or single element (should return 0)
- All elements form good pairs (minimum bad pairs)
- All elements form bad pairs (maximum bad pairs)
- Negative numbers in the array
Solution: The current implementation handles these naturally, but if modifying the code, ensure:
if len(nums) <= 1:
return 0 # No pairs possible
Which technique can we use to find the middle of a linked list?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
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!