LeetCode 2521 - Distinct Prime Factors of Product of Array
The problem asks us to compute the number of distinct prime factors in the product of an array of positive integers, nums.
Difficulty: 🟡 Medium
Topics: Array, Hash Table, Math, Number Theory
Solution
Problem Understanding
The problem asks us to compute the number of distinct prime factors in the product of an array of positive integers, nums. In simpler terms, we are given a list of numbers, and we want to know how many unique prime numbers appear if we were to multiply all the numbers together and factorize the resulting product.
The input nums is an array of integers, where each integer is at least 2 and at most 1000, and the array can have up to 10,000 elements. This constraint ensures that individual numbers are small, but their product could be extremely large - far too large to compute directly. Therefore, a naive approach that multiplies all numbers and then factorizes the result will fail due to integer overflow or performance limitations.
The expected output is a single integer: the count of unique prime numbers that divide the product of all elements in the array.
Important observations include:
- Each number is between 2 and 1000, so all prime factors are also ≤ 1000.
- Repeated primes in different numbers should only be counted once.
- Multiplying all numbers explicitly is unnecessary and impractical because of overflow.
Edge cases that could challenge a naive solution include:
- All numbers are powers of the same prime (e.g.,
[2,4,8,16]), where the product has only one distinct prime factor. - Numbers are distinct primes themselves.
- Numbers with overlapping prime factors (e.g.,
[6,10,15]).
Approaches
The brute-force approach is to first multiply all numbers in nums to form the product, then factorize the product to count distinct prime factors. This is correct mathematically but impractical due to the enormous size of the product for large arrays. It also requires high-precision arithmetic to avoid overflow.
The optimal approach leverages the key observation that all numbers are ≤ 1000. We can precompute all prime numbers up to 1000 using the Sieve of Eratosthenes. Then, for each number in nums, we factorize it directly and insert its prime factors into a set to track unique primes. This avoids multiplying the numbers and keeps the operation efficient, since each number is small and factorization is fast.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n * m) | O(1) | Multiply all numbers (m = number of bits in product) then factorize, impractical due to overflow |
| Optimal | O(n * √maxNum) | O(p) | Factorize each number directly using primes up to maxNum (≤1000), store in set (p = number of distinct primes ≤1000) |
Algorithm Walkthrough
- Generate all prime numbers up to 1000 using the Sieve of Eratosthenes. This allows efficient factorization for numbers in
nums. - Initialize an empty set,
distinct_primes, to store unique prime factors. - Iterate through each number
numinnums. - For each
num, iterate through the list of primes:
- If the prime divides
num, add it todistinct_primes. - Continue dividing
numby the prime until it is no longer divisible. - Stop checking further primes if
numbecomes 1.
- After processing all numbers, return the size of
distinct_primes.
Why it works: Each prime factor from each number is inserted exactly once into the set, ensuring uniqueness. Because we factorize each number directly, we avoid the impractical multiplication of large numbers. The sieve guarantees that we consider all possible primes that could appear.
Python Solution
from typing import List
class Solution:
def distinctPrimeFactors(self, nums: List[int]) -> int:
def sieve(n: int) -> List[int]:
is_prime = [True] * (n + 1)
is_prime[0] = is_prime[1] = False
for i in range(2, int(n**0.5) + 1):
if is_prime[i]:
for j in range(i*i, n + 1, i):
is_prime[j] = False
return [i for i, val in enumerate(is_prime) if val]
primes = sieve(1000)
distinct_primes = set()
for num in nums:
for prime in primes:
if prime * prime > num:
break
if num % prime == 0:
distinct_primes.add(prime)
while num % prime == 0:
num //= prime
if num > 1:
distinct_primes.add(num)
return len(distinct_primes)
The Python implementation first precomputes all primes up to 1000 using the sieve. Then, for each number in the array, it checks divisibility by each prime and records unique prime factors. If a number itself is a prime larger than the square root of its original value, it is added directly to the set.
Go Solution
func distinctPrimeFactors(nums []int) int {
sieve := func(n int) []int {
isPrime := make([]bool, n+1)
for i := 2; i <= n; i++ {
isPrime[i] = true
}
for i := 2; i*i <= n; i++ {
if isPrime[i] {
for j := i * i; j <= n; j += i {
isPrime[j] = false
}
}
}
primes := []int{}
for i := 2; i <= n; i++ {
if isPrime[i] {
primes = append(primes, i)
}
}
return primes
}
primes := sieve(1000)
distinct := make(map[int]struct{})
for _, num := range nums {
temp := num
for _, prime := range primes {
if prime*prime > temp {
break
}
if temp%prime == 0 {
distinct[prime] = struct{}{}
for temp%prime == 0 {
temp /= prime
}
}
}
if temp > 1 {
distinct[temp] = struct{}{}
}
}
return len(distinct)
}
The Go solution follows the same logic as Python. It uses a map with empty structs to simulate a set for storing distinct primes. The sieve and factorization logic is equivalent, adapted to Go's syntax and types.
Worked Examples
Example 1: nums = [2,4,3,7,10,6]
Step through:
| num | distinct_primes after processing |
|---|---|
| 2 | {2} |
| 4 | {2} |
| 3 | {2, 3} |
| 7 | {2, 3, 7} |
| 10 | {2, 3, 5, 7} |
| 6 | {2, 3, 5, 7} |
Output: 4
Example 2: nums = [2,4,8,16]
Step through:
| num | distinct_primes after processing |
|---|---|
| 2 | {2} |
| 4 | {2} |
| 8 | {2} |
| 16 | {2} |
Output: 1
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n * √maxNum) | For each number, we iterate through primes up to its square root, n = length of nums, maxNum = 1000 |
| Space | O(p) | p = number of primes ≤ 1000 stored in sieve and set for distinct factors |
The sieve generation is O(maxNum * log log maxNum), negligible compared to n * √maxNum for n up to 10,000.
Test Cases
# Provided examples
assert Solution().distinctPrimeFactors([2,4,3,7,10,6]) == 4 # mixed primes and composites
assert Solution().distinctPrimeFactors([2,4,8,16]) == 1 # powers of same prime
# Additional cases
assert Solution().distinctPrimeFactors([2,3,5,7,11,13]) == 6 # all primes
assert Solution().distinctPrimeFactors([6,10,15]) == 3 # overlapping prime factors
assert Solution().distinctPrimeFactors([997]) == 1 # single large prime
assert Solution().distinctPrimeFactors([1000,1000]) == 3 # large composite 2^3 * 5^3
assert Solution().distinctPrimeFactors([2]*10000) == 1 # repeated prime 2, large n
| Test | Why |
|---|---|
[2,4,3,7,10,6] |
Mixed numbers with multiple distinct primes |
[2,4,8,16] |
Powers of the same prime, only one distinct factor |
[2,3,5,7,11,13] |
All distinct primes |
[6,10,15] |
Numbers sharing some primes |
[997] |
Single prime edge case |
[1000,1000] |
Large composite numbers |
[2]*10000 |
Large array size with repeated primes |
Edge Cases
One important edge case is when all numbers in the array are powers of a single prime. For example, `[2,4,