An ugly number is a positive integer that is divisible by a
, b
, or c
.
Given four integers n
, a
, b
, and c
, return the nth
ugly number.
Example 1:
Input: n = 3, a = 2, b = 3, c = 5 Output: 4 Explanation: The ugly numbers are 2, 3, 4, 5, 6, 8, 9, 10... The 3rd is 4.
Example 2:
Input: n = 4, a = 2, b = 3, c = 4 Output: 6 Explanation: The ugly numbers are 2, 3, 4, 6, 8, 9, 10, 12... The 4th is 6.
Example 3:
Input: n = 5, a = 2, b = 11, c = 13 Output: 10 Explanation: The ugly numbers are 2, 4, 6, 8, 10, 11, 12, 13... The 5th is 10.
Constraints:
1 <= n, a, b, c <= 109
1 <= a * b * c <= 1018
[1, 2 * 109]
.This problem asks us to find the nth ugly number, where an ugly number is a positive integer divisible by at least one of three given integers (a, b, c). A direct approach of generating ugly numbers until we reach the nth one would be inefficient for large n. Instead, we employ a binary search strategy combined with the inclusion-exclusion principle.
1. Inclusion-Exclusion Principle:
The core idea is to efficiently count the number of ugly numbers less than or equal to a given number x
. We can't simply sum the counts of numbers divisible by a
, b
, and c
individually, because some numbers might be divisible by multiple of these numbers. The inclusion-exclusion principle helps us correct for this overcounting:
a
, b
, or c
individually: ⌊x/a⌋ + ⌊x/b⌋ + ⌊x/c⌋
a
, b
, c
: ⌊x/lcm(a,b)⌋ + ⌊x/lcm(a,c)⌋ + ⌊x/lcm(b,c)⌋
(lcm = least common multiple)a
, b
, c
): ⌊x/lcm(a,b,c)⌋
The formula becomes:
count(x) = ⌊x/a⌋ + ⌊x/b⌋ + ⌊x/c⌋ - ⌊x/lcm(a,b)⌋ - ⌊x/lcm(a,c)⌋ - ⌊x/lcm(b,c)⌋ + ⌊x/lcm(a,b,c)⌋
2. Binary Search:
Now that we have a way to count ugly numbers up to a given x
, we can use binary search to find the smallest x
such that count(x) >= n
.
left = 1
, right = 2 * 10^9
(as per problem constraints).mid = (left + right) / 2
.count(mid)
using the inclusion-exclusion formula.count(mid) >= n
, the nth ugly number is in the range [left, mid]
, so we update right = mid
.[mid + 1, right]
, so we update left = mid + 1
.left == right
, at which point left
(or right
) is the nth ugly number.3. Time and Space Complexity:
Time Complexity: The binary search takes O(log m) time, where m is the upper bound (2 * 10^9). The calculation of count(x)
within each iteration is O(1), as it involves a fixed number of arithmetic operations. Therefore, the overall time complexity is O(log m).
Space Complexity: The algorithm uses a constant amount of extra space for variables, so the space complexity is O(1).
Code Implementation (Python):
import math
def gcd(a, b):
if b == 0:
return a
return gcd(b, a % b)
def lcm(a, b):
return (a * b) // gcd(a, b)
class Solution:
def nthUglyNumber(self, n: int, a: int, b: int, c: int) -> int:
ab = lcm(a, b)
bc = lcm(b, c)
ac = lcm(a, c)
abc = lcm(a, bc) # lcm(a, b, c)
left, right = 1, 2 * 10**9
while left < right:
mid = (left + right) // 2
count = mid // a + mid // b + mid // c - mid // ab - mid // bc - mid // ac + mid // abc
if count >= n:
right = mid
else:
left = mid + 1
return left
The code in other languages (Java, C++, Go, TypeScript) follows the same algorithmic logic, with adjustments for language-specific syntax and data types (e.g., using long
or bigint
for larger numbers in Java, C++, Go and TypeScript). The gcd
and lcm
helper functions are implemented similarly across all languages.