Facebook Pixel

2364. Count Number of Bad Pairs

MediumArrayHash TableMathCounting
Leetcode Link

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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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 (from 0 to i-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] equals i - 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:

  1. Initialize a Counter: We use a hash table cnt to count occurrences of i - nums[i] values as we iterate through the array.

  2. Iterate Through the Array: For each position i with value nums[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 position i
    • Add to answer: ans += i - cnt[key]
    • Update counter: Increment cnt[key] by 1 for future iterations
  3. 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, update cnt[-4] = 1
  • Position 1: key = 1 - 1 = 0, bad pairs = 1 - 0 = 1, update cnt[0] = 1
  • Position 2: key = 2 - 3 = -1, bad pairs = 2 - 0 = 2, update cnt[-1] = 1
  • Position 3: key = 3 - 3 = 0, bad pairs = 3 - 1 = 2 (one good pair with position 1), update cnt[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 Evaluator

Example 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=1j-i = 1, nums[j]-nums[i] = 2-1 = 11 = 1 (good pair)
  • Pair (0,2): i=0, j=2j-i = 2, nums[j]-nums[i] = 5-1 = 42 ≠ 4 (bad pair)
  • Pair (1,2): i=1, j=2j-i = 1, nums[j]-nums[i] = 5-2 = 31 ≠ 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
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which technique can we use to find the middle of a linked list?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More