Facebook Pixel

2748. Number of Beautiful Pairs

EasyArrayHash TableMathCountingNumber Theory
Leetcode Link

Problem Description

You are given a 0-indexed integer array nums. Your task is to count the number of "beautiful pairs" in this array.

A pair of indices (i, j) where 0 <= i < j < nums.length is considered beautiful if:

  • The first digit of nums[i] and the last digit of nums[j] are coprime

Two integers x and y are coprime if their greatest common divisor (GCD) equals 1. In other words, they share no common divisors other than 1.

For example:

  • If nums[i] = 234, the first digit is 2
  • If nums[j] = 567, the last digit is 7
  • Since gcd(2, 7) = 1, this would be a beautiful pair

The problem asks you to return the total count of all beautiful pairs in the array.

Key Points:

  • You need to check pairs where i < j (earlier index paired with later index)
  • Extract the first digit of nums[i] and last digit of nums[j]
  • Check if these two digits are coprime (their GCD is 1)
  • Count all such valid pairs
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The naive approach would be to check every pair (i, j) where i < j, extract the first digit of nums[i] and last digit of nums[j], then check if they're coprime. This would take O(n²) time.

However, we can optimize this by observing a key insight: the first digit of any number can only be one of 9 values (1-9), and the last digit can only be one of 10 values (0-9). This limited range allows us to use counting.

The clever idea is to process the array from left to right. For each number at position j, we want to count how many previous numbers (at positions i < j) have a first digit that is coprime with the current number's last digit.

Since we only care about first digits of previous numbers, we can maintain a frequency array cnt[10] where cnt[d] stores how many numbers we've seen so far that have d as their first digit.

For each number x in the array:

  1. We check its last digit (x % 10)
  2. For each possible first digit value y (0-9), if we've seen numbers with that first digit before (cnt[y] > 0) and gcd(x % 10, y) == 1, then we've found cnt[y] beautiful pairs
  3. After processing x as the second element of potential pairs, we add it to our count by incrementing cnt[first_digit_of_x]

This way, we process each element once and for each element we only check 10 possible digit values, giving us O(n) time complexity instead of O(n²).

Learn more about Math patterns.

Solution Approach

We implement the counting approach using an array cnt of size 10 to track the frequency of first digits from previously processed numbers.

Step-by-step implementation:

  1. Initialize the counter array: Create cnt = [0] * 10 to store counts of first digits (indices 0-9).

  2. Initialize answer: Set ans = 0 to accumulate the count of beautiful pairs.

  3. Process each number: For each number x in nums:

    a. Find beautiful pairs with x as the second element:

    • Extract the last digit of x using x % 10
    • Iterate through all possible first digits y from 0 to 9
    • If cnt[y] > 0 (we've seen numbers with first digit y) and gcd(x % 10, y) == 1 (the digits are coprime), add cnt[y] to the answer

    b. Update the counter for future pairs:

    • Extract the first digit of x by converting to string: int(str(x)[0])
    • Increment cnt[first_digit] by 1
  4. Return the result: After processing all numbers, return ans.

Example walkthrough:

If nums = [25, 37, 14]:

  • Process 25: No previous numbers, so no pairs. Update cnt[2] = 1
  • Process 37: Last digit is 7. Check if 7 is coprime with first digit 2 (from 25). Since gcd(7, 2) = 1, we found 1 pair. Update cnt[3] = 1
  • Process 14: Last digit is 4. Check coprimality with first digits 2 and 3. gcd(4, 2) = 2 ≠ 1 (not coprime), gcd(4, 3) = 1 (coprime), so we found 1 more pair. Update cnt[1] = 1
  • Total: 2 beautiful pairs

The algorithm efficiently counts beautiful pairs in O(n) time with O(1) space (since the cnt array has fixed size 10).

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 = [11, 21, 12]:

Initial Setup:

  • cnt = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] (tracks frequency of first digits)
  • ans = 0 (counts beautiful pairs)

Step 1: Process nums[0] = 11

  • Last digit: 11 % 10 = 1
  • Check all possible first digits (0-9) in cnt:
    • All counts are 0, so no pairs can be formed yet
  • First digit of 11: int(str(11)[0]) = 1
  • Update: cnt[1] = 1
  • Current state: cnt = [0, 1, 0, 0, 0, 0, 0, 0, 0, 0], ans = 0

Step 2: Process nums[1] = 21

  • Last digit: 21 % 10 = 1
  • Check all possible first digits (0-9) in cnt:
    • cnt[1] = 1 (we have one number starting with 1)
    • Check if gcd(1, 1) = 1? Yes! So we add cnt[1] = 1 to answer
    • ans = 0 + 1 = 1
  • First digit of 21: int(str(21)[0]) = 2
  • Update: cnt[2] = 1
  • Current state: cnt = [0, 1, 1, 0, 0, 0, 0, 0, 0, 0], ans = 1

Step 3: Process nums[2] = 12

  • Last digit: 12 % 10 = 2
  • Check all possible first digits (0-9) in cnt:
    • cnt[1] = 1: Check gcd(2, 1) = 1? Yes! Add 1 to answer
    • cnt[2] = 1: Check gcd(2, 2) = 2? No, not coprime
    • ans = 1 + 1 = 2
  • First digit of 12: int(str(12)[0]) = 1
  • Update: cnt[1] = 2
  • Final state: cnt = [0, 2, 1, 0, 0, 0, 0, 0, 0, 0], ans = 2

Result: We found 2 beautiful pairs:

  • Pair (0, 1): nums[0]=11 (first digit 1) and nums[1]=21 (last digit 1), gcd(1,1)=1
  • Pair (0, 2): nums[0]=11 (first digit 1) and nums[2]=12 (last digit 2), gcd(1,2)=1

Solution Implementation

1from typing import List
2from math import gcd
3
4class Solution:
5    def countBeautifulPairs(self, nums: List[int]) -> int:
6        # Track count of first digits seen so far
7        first_digit_count = [0] * 10
8        beautiful_pairs_count = 0
9      
10        # Process each number in the array
11        for current_num in nums:
12            # Get the last digit of current number
13            last_digit = current_num % 10
14          
15            # Check against all possible first digits (0-9)
16            for first_digit in range(10):
17                # If we've seen numbers with this first digit before
18                # and it forms a coprime pair with current number's last digit
19                if first_digit_count[first_digit] > 0 and gcd(last_digit, first_digit) == 1:
20                    # Add the count of numbers with this first digit
21                    beautiful_pairs_count += first_digit_count[first_digit]
22          
23            # Get the first digit of current number and update its count
24            # Convert to string to easily access first character
25            current_first_digit = int(str(current_num)[0])
26            first_digit_count[current_first_digit] += 1
27      
28        return beautiful_pairs_count
29
1class Solution {
2    /**
3     * Counts the number of beautiful pairs in the array.
4     * A beautiful pair (i, j) where i < j exists when gcd(firstDigit(nums[i]), lastDigit(nums[j])) == 1
5     * 
6     * @param nums the input array of positive integers
7     * @return the count of beautiful pairs
8     */
9    public int countBeautifulPairs(int[] nums) {
10        // Array to store count of first digits (0-9) seen so far
11        int[] firstDigitCount = new int[10];
12        int beautifulPairsCount = 0;
13      
14        // Iterate through each number in the array
15        for (int currentNumber : nums) {
16            // Check current number's last digit against all previous first digits
17            int lastDigit = currentNumber % 10;
18          
19            for (int firstDigit = 0; firstDigit < 10; ++firstDigit) {
20                // If we've seen this first digit before and it's coprime with current last digit
21                if (firstDigitCount[firstDigit] > 0 && gcd(lastDigit, firstDigit) == 1) {
22                    // Add the count of numbers with this first digit
23                    beautifulPairsCount += firstDigitCount[firstDigit];
24                }
25            }
26          
27            // Extract the first digit of current number
28            int tempNumber = currentNumber;
29            while (tempNumber > 9) {
30                tempNumber /= 10;
31            }
32            // Increment count for this first digit
33            ++firstDigitCount[tempNumber];
34        }
35      
36        return beautifulPairsCount;
37    }
38  
39    /**
40     * Calculates the greatest common divisor of two numbers using Euclidean algorithm
41     * 
42     * @param a first number
43     * @param b second number
44     * @return the GCD of a and b
45     */
46    private int gcd(int a, int b) {
47        return b == 0 ? a : gcd(b, a % b);
48    }
49}
50
1class Solution {
2public:
3    int countBeautifulPairs(vector<int>& nums) {
4        // Array to count occurrences of first digits (0-9)
5        int firstDigitCount[10]{};
6        int beautifulPairsCount = 0;
7      
8        // Iterate through each number in the array
9        for (int currentNum : nums) {
10            // Check all possible first digits (0-9) from previous numbers
11            for (int firstDigit = 0; firstDigit < 10; ++firstDigit) {
12                // If there are numbers with this first digit and 
13                // GCD of current number's last digit with this first digit is 1
14                if (firstDigitCount[firstDigit] && gcd(currentNum % 10, firstDigit) == 1) {
15                    // Add the count of numbers with this first digit to result
16                    beautifulPairsCount += firstDigitCount[firstDigit];
17                }
18            }
19          
20            // Extract the first digit of current number
21            int tempNum = currentNum;
22            while (tempNum > 9) {
23                tempNum /= 10;
24            }
25          
26            // Increment count for this first digit
27            ++firstDigitCount[tempNum];
28        }
29      
30        return beautifulPairsCount;
31    }
32};
33
1/**
2 * Counts the number of beautiful pairs in the array.
3 * A beautiful pair (i, j) where i < j exists when gcd(first digit of nums[i], last digit of nums[j]) = 1
4 * @param nums - Array of positive integers
5 * @returns Number of beautiful pairs
6 */
7function countBeautifulPairs(nums: number[]): number {
8    // Array to count occurrences of first digits (0-9)
9    const firstDigitCount: number[] = Array(10).fill(0);
10    let beautifulPairsCount: number = 0;
11  
12    // Process each number in the array
13    for (const currentNumber of nums) {
14        // Check current number's last digit against all previously seen first digits
15        const lastDigit: number = currentNumber % 10;
16      
17        for (let firstDigit: number = 0; firstDigit < 10; firstDigit++) {
18            // If we've seen this first digit before and it forms a coprime pair with current last digit
19            if (firstDigitCount[firstDigit] > 0 && gcd(lastDigit, firstDigit) === 1) {
20                beautifulPairsCount += firstDigitCount[firstDigit];
21            }
22        }
23      
24        // Extract the first digit of current number
25        let tempNumber: number = currentNumber;
26        while (tempNumber > 9) {
27            tempNumber = Math.floor(tempNumber / 10);
28        }
29      
30        // Increment count for this first digit
31        firstDigitCount[tempNumber]++;
32    }
33  
34    return beautifulPairsCount;
35}
36
37/**
38 * Calculates the greatest common divisor of two numbers using Euclidean algorithm
39 * @param a - First number
40 * @param b - Second number
41 * @returns Greatest common divisor of a and b
42 */
43function gcd(a: number, b: number): number {
44    // Base case: if b is 0, gcd is a
45    if (b === 0) {
46        return a;
47    }
48    // Recursive case: gcd(a, b) = gcd(b, a mod b)
49    return gcd(b, a % b);
50}
51

Time and Space Complexity

Time Complexity: O(n × k) where n is the length of the array nums and k = 10 (constant representing possible digits 0-9).

  • The outer loop iterates through all n elements in nums
  • For each element, the inner loop iterates through 10 possible digits (0-9)
  • Inside the inner loop, gcd(x % 10, y) operation takes O(log min(x % 10, y)) time, but since both values are single digits (0-9), this is effectively O(1)
  • Converting x to string to get the first digit str(x)[0] takes O(log M) time where M is the maximum value in nums
  • Overall: O(n × (10 + log M)) = O(n × (k + log M))

Space Complexity: O(k) where k = 10 (constant).

  • The cnt array has a fixed size of 10 to store counts of first digits
  • Converting integer x to string creates temporary space of O(log M) where M is the maximum value
  • Overall: O(10 + log M) = O(k + log M)

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

Common Pitfalls

Pitfall 1: Incorrect Order of Operations

Issue: A common mistake is updating the first_digit_count array before checking for beautiful pairs with the current number. This would incorrectly count a number pairing with itself.

Incorrect Implementation:

for current_num in nums:
    # WRONG: Updating count first
    current_first_digit = int(str(current_num)[0])
    first_digit_count[current_first_digit] += 1
  
    # Then checking pairs - this includes self-pairing!
    last_digit = current_num % 10
    for first_digit in range(10):
        if first_digit_count[first_digit] > 0 and gcd(last_digit, first_digit) == 1:
            beautiful_pairs_count += first_digit_count[first_digit]

Solution: Always check for pairs first, then update the counter. This ensures we only consider pairs where i < j.

Pitfall 2: Edge Case with GCD of Zero

Issue: When checking gcd(last_digit, first_digit), if first_digit is 0, the GCD calculation might behave unexpectedly. While gcd(n, 0) = n mathematically, this could lead to incorrect results if not handled properly.

Solution: Either handle zero explicitly or rely on Python's math.gcd which correctly handles gcd(n, 0) = n. Since we need gcd = 1 for coprime numbers, and gcd(n, 0) = n which is only 1 when n = 1, the logic works correctly.

Pitfall 3: Extracting First Digit of Single-Digit Numbers

Issue: For single-digit numbers, both first and last digits are the same. The string conversion str(current_num)[0] works correctly, but developers might overthink this case.

Example: For nums = [7], the first digit is 7 and last digit is 7. No pairs exist since we need at least two elements.

Solution: The current implementation handles this naturally - no special case needed.

Pitfall 4: Misunderstanding Pair Direction

Issue: Confusing which number provides the first digit and which provides the last digit. The problem states: first digit of nums[i] and last digit of nums[j] where i < j.

Incorrect Understanding: Checking first digit of nums[j] against last digit of nums[i].

Solution: Remember the pattern - as we iterate through the array, for each current number (acting as nums[j]), we use its last digit and check against first digits of all previous numbers (acting as nums[i]).

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

How does merge sort divide the problem into subproblems?


Recommended Readings

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

Load More