Facebook Pixel

1814. Count Nice Pairs in an Array

MediumArrayHash TableMathCounting
Leetcode Link

Problem Description

You have an array nums containing non-negative integers. For any non-negative integer x, we define rev(x) as the number obtained by reversing its digits. For example, rev(123) = 321 and rev(120) = 21 (leading zeros are removed).

A pair of indices (i, j) is called a "nice pair" if it meets these two conditions:

  1. The indices satisfy 0 <= i < j < nums.length (i comes before j)
  2. The equation nums[i] + rev(nums[j]) == nums[j] + rev(nums[i]) holds true

Your task is to count how many nice pairs exist in the array. Since the answer might be very large, return the result modulo 10^9 + 7.

The key insight for solving this problem involves transforming the equation. When nums[i] + rev(nums[j]) = nums[j] + rev(nums[i]), we can rearrange it to get nums[i] - rev(nums[i]) = nums[j] - rev(nums[j]). This means two indices form a nice pair if the difference between each number and its reverse is the same.

The solution uses a hash table to count how many numbers have the same difference value (x - rev(x)). For each group of numbers with the same difference, we can form n * (n-1) / 2 pairs where n is the count of numbers in that group. This formula calculates the number of ways to choose 2 items from n items, which gives us all possible pairs.

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

Intuition

The original condition nums[i] + rev(nums[j]) == nums[j] + rev(nums[i]) looks complex at first glance. The breakthrough comes when we realize we can rearrange this equation algebraically.

By moving terms around, we get:

  • nums[i] + rev(nums[j]) = nums[j] + rev(nums[i])
  • nums[i] - nums[j] = rev(nums[i]) - rev(nums[j])
  • nums[i] - rev(nums[i]) = nums[j] - rev(nums[j])

This transformation reveals something crucial: for any pair (i, j) to be nice, the value nums[i] - rev(nums[i]) must equal nums[j] - rev(nums[j]). In other words, both indices must produce the same difference when we subtract their reverse from the original number.

This observation transforms our problem from checking all possible pairs (which would be O(n²)) to a counting problem. We can compute x - rev(x) for each element in the array just once. If multiple elements produce the same difference value, any two of them can form a nice pair.

For example, if three numbers all have the same difference value, we can form 3 * 2 / 2 = 3 nice pairs from them. This is the combination formula C(n, 2) = n * (n-1) / 2, which counts the number of ways to choose 2 items from n items.

The hash table approach naturally emerges from this insight: we count how many times each difference value appears, then for each group with count v, we add v * (v-1) / 2 to our answer. This efficiently counts all nice pairs without having to check every possible pair individually.

Learn more about Math patterns.

Solution Approach

The implementation follows the mathematical transformation we discovered: for a nice pair (i, j), the condition nums[i] - rev(nums[i]) = nums[j] - rev(nums[j]) must hold.

First, we implement a helper function rev(x) to reverse a number's digits:

  • Initialize result y = 0
  • While x has digits remaining:
    • Extract the last digit using x % 10
    • Append it to y by multiplying y by 10 and adding the digit
    • Remove the last digit from x using integer division x //= 10
  • Return the reversed number y

Next, we transform each element in the array:

  • For each number x in nums, calculate x - rev(x)
  • Use Python's Counter to count the frequency of each difference value
  • This creates a hash table where keys are difference values and values are their counts

Finally, we count the nice pairs:

  • For each unique difference value with count v in our hash table:
    • Calculate the number of pairs using the combination formula v * (v - 1) // 2
    • This counts all possible ways to choose 2 elements from v elements
  • Sum up all these pair counts
  • Apply modulo 10^9 + 7 to the final result to prevent overflow

The time complexity is O(n * k) where n is the length of the array and k is the average number of digits in the numbers (for the reversal operation). The space complexity is O(n) for the hash table storage.

This approach efficiently reduces what would be an O(n²) brute force solution to a linear time solution by recognizing the mathematical pattern and using appropriate data structures.

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 the solution with nums = [42, 11, 1, 97].

Step 1: Calculate reversed numbers

  • rev(42) = 24 (reverse digits of 42)
  • rev(11) = 11 (palindrome stays the same)
  • rev(1) = 1 (single digit stays the same)
  • rev(97) = 79 (reverse digits of 97)

Step 2: Calculate differences for each number For each number, compute x - rev(x):

  • 42 - rev(42) = 42 - 24 = 18
  • 11 - rev(11) = 11 - 11 = 0
  • 1 - rev(1) = 1 - 1 = 0
  • 97 - rev(97) = 97 - 79 = 18

Step 3: Count frequencies of each difference Using a hash table to count occurrences:

  • Difference 18: appears 2 times (from indices 0 and 3)
  • Difference 0: appears 2 times (from indices 1 and 2)

Step 4: Calculate nice pairs for each group For each difference value with count v, we can form v * (v-1) / 2 pairs:

  • Difference 18 with count 2: 2 * (2-1) / 2 = 1 pair
    • This represents the pair (0, 3) since nums[0] and nums[3] both have difference 18
  • Difference 0 with count 2: 2 * (2-1) / 2 = 1 pair
    • This represents the pair (1, 2) since nums[1] and nums[2] both have difference 0

Step 5: Sum up total pairs Total nice pairs = 1 + 1 = 2

Verification: Let's verify our nice pairs satisfy the original condition:

  • Pair (0, 3): nums[0] + rev(nums[3]) = 42 + 79 = 121 and nums[3] + rev(nums[0]) = 97 + 24 = 121
  • Pair (1, 2): nums[1] + rev(nums[2]) = 11 + 1 = 12 and nums[2] + rev(nums[1]) = 1 + 11 = 12

The answer is 2 nice pairs.

Solution Implementation

1class Solution:
2    def countNicePairs(self, nums: List[int]) -> int:
3        def reverse_number(num):
4            """
5            Reverse the digits of a given number.
6            For example: 123 -> 321, 1200 -> 21
7            """
8            reversed_num = 0
9            while num:
10                reversed_num = reversed_num * 10 + num % 10
11                num //= 10
12            return reversed_num
13      
14        # Transform the problem: nums[i] + rev(nums[j]) == nums[j] + rev(nums[i])
15        # Can be rewritten as: nums[i] - rev(nums[i]) == nums[j] - rev(nums[j])
16        # Count frequency of each (num - reverse(num)) value
17        difference_counter = Counter(num - reverse_number(num) for num in nums)
18      
19        # Define modulo for the result as per problem constraints
20        MOD = 10**9 + 7
21      
22        # For each group with the same difference value, calculate number of pairs
23        # Number of pairs from n elements is n * (n - 1) / 2 (combination formula)
24        total_pairs = sum(count * (count - 1) // 2 for count in difference_counter.values())
25      
26        return total_pairs % MOD
27
1class Solution {
2    /**
3     * Counts the number of nice pairs in the array.
4     * A nice pair (i, j) where i < j satisfies: nums[i] + rev(nums[j]) == nums[j] + rev(nums[i])
5     * This can be rearranged to: nums[i] - rev(nums[i]) == nums[j] - rev(nums[j])
6     * 
7     * @param nums the input array of positive integers
8     * @return the count of nice pairs modulo 10^9 + 7
9     */
10    public int countNicePairs(int[] nums) {
11        // Map to store frequency of (num - reverse(num)) values
12        Map<Integer, Integer> frequencyMap = new HashMap<>();
13      
14        // Calculate num - reverse(num) for each element and count frequencies
15        for (int num : nums) {
16            int difference = num - rev(num);
17            frequencyMap.merge(difference, 1, Integer::sum);
18        }
19      
20        // Modulo value as per problem requirement
21        final int MOD = (int) 1e9 + 7;
22      
23        // Calculate total nice pairs using combination formula C(n, 2) = n * (n - 1) / 2
24        long totalPairs = 0;
25        for (int frequency : frequencyMap.values()) {
26            // For each group with same difference value, calculate pairs
27            totalPairs = (totalPairs + (long) frequency * (frequency - 1) / 2) % MOD;
28        }
29      
30        return (int) totalPairs;
31    }
32  
33    /**
34     * Reverses the digits of a given integer.
35     * For example: 123 becomes 321, 1000 becomes 1
36     * 
37     * @param x the integer to reverse
38     * @return the reversed integer
39     */
40    private int rev(int x) {
41        int reversed = 0;
42      
43        // Extract digits from right to left and build reversed number
44        while (x > 0) {
45            reversed = reversed * 10 + x % 10;
46            x /= 10;
47        }
48      
49        return reversed;
50    }
51}
52
1class Solution {
2public:
3    int countNicePairs(vector<int>& nums) {
4        // Lambda function to reverse the digits of a number
5        auto reverseNumber = [](int number) {
6            int reversed = 0;
7            while (number > 0) {
8                reversed = reversed * 10 + number % 10;
9                number /= 10;
10            }
11            return reversed;
12        };
13      
14        // Map to store frequency of (num - reverse(num)) values
15        // Key insight: nums[i] + rev(nums[j]) == nums[j] + rev(nums[i])
16        // can be rewritten as: nums[i] - rev(nums[i]) == nums[j] - rev(nums[j])
17        unordered_map<int, int> frequencyMap;
18      
19        // Calculate difference between each number and its reverse
20        for (int& number : nums) {
21            int difference = number - reverseNumber(number);
22            frequencyMap[difference]++;
23        }
24      
25        // Count total number of nice pairs
26        long long totalPairs = 0;
27        const int MOD = 1e9 + 7;
28      
29        // For each group with same difference value, calculate combinations C(n, 2)
30        // Number of pairs from n elements = n * (n - 1) / 2
31        for (auto& [key, frequency] : frequencyMap) {
32            totalPairs = (totalPairs + 1LL * frequency * (frequency - 1) / 2) % MOD;
33        }
34      
35        return totalPairs;
36    }
37};
38
1/**
2 * Counts the number of nice pairs in the array.
3 * A nice pair (i, j) where i < j satisfies: nums[i] + rev(nums[j]) == nums[j] + rev(nums[i])
4 * This can be rewritten as: nums[i] - rev(nums[i]) == nums[j] - rev(nums[j])
5 * 
6 * @param nums - Array of non-negative integers
7 * @returns Number of nice pairs modulo 10^9 + 7
8 */
9function countNicePairs(nums: number[]): number {
10    /**
11     * Reverses the digits of a number
12     * Example: 123 -> 321, 100 -> 1
13     * 
14     * @param x - The number to reverse
15     * @returns The reversed number
16     */
17    const reverseNumber = (x: number): number => {
18        let reversed = 0;
19      
20        while (x > 0) {
21            // Extract the last digit and append it to the reversed number
22            reversed = reversed * 10 + (x % 10);
23            // Remove the last digit from x
24            x = Math.floor(x / 10);
25        }
26      
27        return reversed;
28    };
29  
30    // Modulo value to prevent integer overflow
31    const MODULO = 10 ** 9 + 7;
32  
33    // Map to store frequency of each difference value (num - reverse(num))
34    const differenceFrequencyMap = new Map<number, number>();
35  
36    // Total count of nice pairs
37    let nicePairsCount = 0;
38  
39    // Iterate through each number in the array
40    for (const currentNum of nums) {
41        // Calculate the difference: num - reverse(num)
42        const difference = currentNum - reverseNumber(currentNum);
43      
44        // Add the count of previous numbers with the same difference
45        // These form nice pairs with the current number
46        nicePairsCount = (nicePairsCount + (differenceFrequencyMap.get(difference) ?? 0)) % MODULO;
47      
48        // Update the frequency of the current difference
49        differenceFrequencyMap.set(difference, (differenceFrequencyMap.get(difference) ?? 0) + 1);
50    }
51  
52    return nicePairsCount;
53}
54

Time and Space Complexity

Time Complexity: O(n × log M)

The time complexity consists of two main parts:

  • The list comprehension iterates through all n elements in nums
  • For each element x, the rev(x) function is called, which takes O(log M) time where M is the maximum value in the array (since the number of digits in x is proportional to log x)
  • Computing x - rev(x) for each element takes O(1) after reversing
  • Creating the Counter takes O(n) time
  • The final sum operation iterates through at most n unique values in the Counter and performs O(1) arithmetic operations for each

Therefore, the dominant operation is the reversal calculation for each element, giving us O(n × log M).

Space Complexity: O(n)

The space complexity is determined by:

  • The Counter object cnt which stores at most n key-value pairs (in the worst case where all x - rev(x) values are unique)
  • The list comprehension creates a temporary list of size n before passing it to Counter
  • The rev function uses O(1) auxiliary space for variables y and x

The dominant space usage is the Counter storage, resulting in O(n) space complexity.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Integer Overflow in Pair Counting

Pitfall: When calculating count * (count - 1) // 2, the multiplication count * (count - 1) can cause integer overflow before the division, especially when count is large. Although Python handles big integers automatically, in other languages or when dealing with very large counts, this intermediate result might exceed integer limits.

Solution: Apply modulo operation during the calculation rather than only at the end:

total_pairs = 0
for count in difference_counter.values():
    total_pairs = (total_pairs + count * (count - 1) // 2) % MOD
return total_pairs

2. Incorrect Reversal Implementation for Numbers Ending in Zeros

Pitfall: A common mistake is not handling trailing zeros correctly when reversing numbers. For example, reversing 1200 should give 21, not 0021. Some implementations might accidentally preserve leading zeros or handle them incorrectly.

Solution: The provided implementation correctly handles this by building the reversed number through arithmetic operations rather than string manipulation:

def reverse_number(num):
    reversed_num = 0
    while num:
        reversed_num = reversed_num * 10 + num % 10
        num //= 10
    return reversed_num

3. Missing Edge Case: Single Element or No Valid Pairs

Pitfall: Not handling arrays with only one element or arrays where no nice pairs exist. The combination formula count * (count - 1) // 2 naturally handles these cases (returns 0 when count = 1), but explicit validation might be overlooked.

Solution: The current implementation handles this correctly through the combination formula. When count = 1, the result is 1 * 0 // 2 = 0, which is correct since you can't form a pair with a single element.

4. Using String Reversal Instead of Numerical Reversal

Pitfall: Using string manipulation to reverse numbers like int(str(num)[::-1]) can be problematic:

  • It's generally slower than arithmetic operations
  • It might not handle edge cases like single digit numbers or zeros correctly
  • Leading zeros in the string representation could cause issues

Solution: Stick to arithmetic operations for reversal as shown in the provided solution, which is both more efficient and handles all edge cases correctly.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Is the following code DFS or BFS?

void search(Node root) {
  if (!root) return;
  visit(root);
  root.visited = true;
  for (Node node in root.adjacent) {
    if (!node.visited) {
      search(node);
    }
  }
}

Recommended Readings

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

Load More