Number of Beautiful Pairs
EasyPrompt
You are given a 0-indexed integer array nums. A pair of indices i, j where 0 <= i < j < nums.length is called beautiful if the first digit of nums[i] and the last digit of nums[j] are coprime.
Return the total number of beautiful pairs in nums.
Two integers x and y are coprime if there is no integer greater than 1 that divides both of them. In other words, x and y are coprime if gcd(x, y) == 1, where gcd(x, y) is the greatest common divisor of x and y.
Example 1:
Input: nums = [2,5,1,4]
Output: 5
Explanation: There are 5 beautiful pairs in nums:
When i = 0 and j = 1: the first digit of nums[0] is 2, and the last digit of nums[1] is 5. We can confirm that 2 and 5 are coprime, since gcd(2,5) == 1.
When i = 0 and j = 2: the first digit of nums[0] is 2, and the last digit of nums[2] is 1. Indeed, gcd(2,1) == 1.
When i = 1 and j = 2: the first digit of nums[1] is 5, and the last digit of nums[2] is 1. Indeed, gcd(5,1) == 1.
When i = 1 and j = 3: the first digit of nums[1] is 5, and the last digit of nums[3] is 4. Indeed, gcd(5,4) == 1.
When i = 2 and j = 3: the first digit of nums[2] is 1, and the last digit of nums[3] is 4. Indeed, gcd(1,4) == 1.
Thus, we return 5.Example 2:
Input: nums = [11,21,12]
Output: 2
Explanation: There are 2 beautiful pairs:
When i = 0 and j = 1: the first digit of nums[0] is 1, and the last digit of nums[1] is 1. Indeed, gcd(1,1) == 1.
When i = 0 and j = 2: the first digit of nums[0] is 1, and the last digit of nums[2] is 2. Indeed, gcd(1,2) == 1.
Thus, we return 2.
Constraints:
2 <= nums.length <= 1001 <= nums[i] <= 9999nums[i] % 10 != 0
Approaches
2 approaches with complexity analysis and trade-offs.
This approach directly translates the problem statement into code. It involves checking every possible pair of indices (i, j) where i < j. For each pair, we extract the first digit of nums[i] and the last digit of nums[j] and then check if they are coprime.
Algorithm
- Initialize a counter
beautifulPairsCountto 0. - Use a nested loop to iterate through all pairs of indices
(i, j)such that0 <= i < j < nums.length. - For each pair
(nums[i], nums[j]):- Extract the first digit of
nums[i]. This can be done by repeatedly dividing the number by 10 until it is a single-digit number. - Extract the last digit of
nums[j]. This is simplynums[j] % 10. - Calculate the greatest common divisor (GCD) of the two extracted digits using the Euclidean algorithm.
- If the GCD is 1, it means the digits are coprime, so increment
beautifulPairsCount.
- Extract the first digit of
- After iterating through all valid pairs, return
beautifulPairsCount.
Walkthrough
The brute-force solution uses two nested loops to generate all unique pairs of indices (i, j) where i is less than j. The outer loop runs from i = 0 to n-2 and the inner loop from j = i + 1 to n-1, where n is the length of the array. Inside the inner loop, for each pair of numbers nums[i] and nums[j], we find the first digit of nums[i] and the last digit of nums[j]. The first digit is found by repeatedly dividing the number by 10. The last digit is found using the modulo operator (% 10). We then compute their greatest common divisor (GCD). If the GCD is 1, the pair is beautiful, and we increment a counter. This process continues until all pairs have been checked.
class Solution { public int countBeautifulPairs(int[] nums) { int count = 0; int n = nums.length; for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { int firstDigit = getFirstDigit(nums[i]); int lastDigit = nums[j] % 10; if (gcd(firstDigit, lastDigit) == 1) { count++; } } } return count; } private int getFirstDigit(int n) { while (n >= 10) { n /= 10; } return n; } private int gcd(int a, int b) { while (b != 0) { int temp = b; b = a % b; a = temp; } return a; }}Complexity
Time
O(N^2). We have two nested loops that iterate through the array, resulting in a quadratic number of pairs to check. The operations inside the loop (digit extraction and GCD) are constant time since the digits are always between 1 and 9.
Space
O(1). We only use a few variables for the loops and calculations, so the extra space required is constant.
Trade-offs
Pros
Simple to understand and implement.
Works well for the given constraints where
nums.length <= 100.
Cons
Inefficient for large input sizes, as its time complexity is quadratic.
An optimized approach is to iterate through the array just once. We can use a frequency map (or a simple array since digits are 1-9) to keep track of the first digits of numbers we have already processed. For each new number, we can quickly find how many beautiful pairs it forms with the numbers seen so far.
Algorithm
- Initialize a counter
beautifulPairsCountto 0. - Initialize a frequency array
firstDigitCountsof size 10 to all zeros. This array will store the counts of the first digits of numbers encountered so far. - Iterate through each
numin the input arraynums. - For the current
num:- Get its last digit,
ld = num % 10. - Iterate through all possible first digits
fdfrom 1 to 9. - If
gcd(fd, ld) == 1, it means any number seen previously withfdas its first digit forms a beautiful pair. Add the countfirstDigitCounts[fd]tobeautifulPairsCount.
- Get its last digit,
- After checking for pairs, update the frequency map for the current number:
- Get the first digit of the current
num,currentFd. - Increment
firstDigitCounts[currentFd].
- Get the first digit of the current
- After iterating through all numbers in
nums, returnbeautifulPairsCount.
Walkthrough
This approach avoids the O(N^2) complexity by making a single pass over the array. The key idea is that as we iterate to an element nums[j], we can count how many preceding elements nums[i] (where i < j) form a beautiful pair with it. To do this efficiently, we maintain a frequency array, firstDigitCounts, of size 10. firstDigitCounts[d] will store how many numbers we have seen so far that have d as their first digit.
When we are at nums[j], we get its last digit, ld. Then, we iterate through all possible first digits fd from 1 to 9. If gcd(fd, ld) == 1, we know that nums[j] forms a beautiful pair with all the previously seen numbers whose first digit is fd. We add firstDigitCounts[fd] to our total count. After we have tallied the pairs for nums[j], we update the frequency array by finding the first digit of nums[j] and incrementing its count. This ensures that nums[j] is considered for subsequent elements.
class Solution { public int countBeautifulPairs(int[] nums) { int count = 0; int[] firstDigitCounts = new int[10]; // To store frequency of first digits (1-9) for (int num : nums) { int lastDigit = num % 10; for (int firstDigit = 1; firstDigit <= 9; firstDigit++) { if (firstDigitCounts[firstDigit] > 0 && gcd(firstDigit, lastDigit) == 1) { count += firstDigitCounts[firstDigit]; } } int currentFirstDigit = getFirstDigit(num); firstDigitCounts[currentFirstDigit]++; } return count; } private int getFirstDigit(int n) { while (n >= 10) { n /= 10; } return n; } private int gcd(int a, int b) { while (b != 0) { int temp = b; b = a % b; a = temp; } return a; }}Complexity
Time
O(N). We iterate through the `nums` array once. The inner loop runs a constant 9 times for each number. All other operations inside the loop are constant time.
Space
O(1). We use an auxiliary array `firstDigitCounts` of a fixed size 10, which does not depend on the input size.
Trade-offs
Pros
Highly efficient with a linear time complexity.
Scales well even for much larger inputs than specified in the constraints.
Cons
Slightly more complex to reason about compared to the brute-force approach.
Solutions
Solution
class Solution {public int countBeautifulPairs(int[] nums) { int[] cnt = new int[10]; int ans = 0; for (int x : nums) { for (int y = 0; y < 10; ++y) { if (cnt[y] > 0 && gcd(x % 10, y) == 1) { ans += cnt[y]; } } while (x > 9) { x /= 10; } ++cnt[x]; } return ans; }private int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }}class Solution {public: int countBeautifulPairs(vector<int> &nums) { int cnt[10]{}; int ans = 0; for (int x : nums) { for (int y = 0; y < 10; ++y) { if (cnt[y] && gcd(x % 10, y) == 1) { ans += cnt[y]; } } while (x > 9) { x /= 10; } ++cnt[x]; } return ans; }};class Solution: def countBeautifulPairs(self, nums: List[int]) -> int: cnt = [0] * 10 ans = 0 for x in nums: for y in range(10): if cnt[y] and gcd(x % 10, y) == 1: ans += cnt[y] cnt[int(str(x)[0])] += 1 return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.