2521. Distinct Prime Factors of Product of Array
Problem Description
You are given an array of positive integers nums
. Your task is to find the number of distinct prime factors that appear in the product of all elements in the array.
A prime number is a number greater than 1 that can only be divided evenly by 1 and itself. For example, 2, 3, 5, 7, 11 are prime numbers.
A number val1
is a factor of another number val2
if val2
divided by val1
gives an integer result (no remainder).
The key insight is that you need to find all unique prime numbers that divide at least one element in the array. When you multiply all elements together to get the product, these prime factors will be part of the prime factorization of that product.
For example, if nums = [2, 4, 3, 7, 10, 6]
:
- The product is
2 × 4 × 3 × 7 × 10 × 6 = 10080
- Breaking down each number into prime factors:
2 = 2
4 = 2²
3 = 3
7 = 7
10 = 2 × 5
6 = 2 × 3
- The distinct prime factors are:
2, 3, 5, 7
- So the answer is
4
The solution uses prime factorization for each number in the array and collects all unique prime factors using a set data structure. For each number, it finds all prime factors by trial division starting from 2, and adds them to the set. The final answer is the size of this set.
Intuition
The key observation is that when we multiply all numbers in the array together, the prime factorization of the product will contain all prime factors from individual numbers. However, we don't actually need to compute this potentially huge product.
Think about it this way: if a prime number p
divides any number in the array, then p
will also divide the product of all numbers. For example, if 2
divides 10
in the array, then 2
will definitely divide the final product regardless of what other numbers are present.
This leads us to a simpler approach: instead of calculating the product first (which could cause overflow), we can find prime factors of each number individually and collect them. Since we only care about distinct prime factors, not their frequencies, we can use a set to automatically handle duplicates.
The process becomes:
- For each number in the array, find all its prime factors
- Add these prime factors to a set (which keeps only unique values)
- The size of the set gives us the count of distinct prime factors
For prime factorization itself, we use trial division: we try dividing by potential factors starting from 2
. When we find a factor i
, we keep dividing by i
until it's no longer a factor (this handles prime powers like 2³
in 8
). We only need to check factors up to √n
because if n
has a factor larger than √n
, it can have at most one such factor, and that factor would be n
itself after removing all smaller prime factors.
This approach is efficient because we avoid computing large products and instead work with each number independently, collecting unique prime factors as we go.
Learn more about Math patterns.
Solution Approach
The solution uses a hash table (set) combined with prime factorization to find all distinct prime factors across the array elements.
Data Structure Used:
- A
set
to store unique prime factors found across all numbers
Algorithm Steps:
-
Initialize an empty set
s
to store distinct prime factors. -
Iterate through each number
n
in the input arraynums
. -
For each number, perform prime factorization:
- Start with the smallest prime candidate
i = 2
- While
i <= n // i
(equivalent toi <= √n
):- Check if
i
is a factor ofn
by testingn % i == 0
- If
i
is a factor:- Add
i
to the set (it's a prime factor) - Completely remove this prime factor by repeatedly dividing:
while n % i == 0: n //= i
- Add
- Increment
i
to check the next potential factor
- Check if
- Start with the smallest prime candidate
-
Handle remaining prime factor:
- After the loop, if
n > 1
, thenn
itself is a prime factor - This happens when
n
has a prime factor greater than√n
- Add this remaining
n
to the set
- After the loop, if
-
Return the result: The size of the set
len(s)
gives the count of distinct prime factors.
Why this works:
- The condition
i <= n // i
ensures we only check divisors up to√n
, which is sufficient because any composite number must have at least one prime factor ≤√n
- The inner
while
loopwhile n % i == 0: n //= i
removes all occurrences of primei
fromn
, effectively extracting the prime and reducingn
- By dividing out each prime factor completely, we ensure that each
i
that dividesn
is actually prime (not composite) - Using a set automatically handles duplicates - if multiple numbers share the same prime factor, it's only counted once
Time Complexity: O(m × √k)
where m
is the length of the array and k
is the maximum value in the array.
Space Complexity: O(p)
where p
is the number of distinct prime factors found.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with nums = [12, 15, 10]
.
Step 1: Initialize empty set
s = {}
Step 2: Process first number (12)
n = 12
, start withi = 2
- Check if
2
divides12
: Yes (12 % 2 == 0)- Add
2
to set:s = {2}
- Remove all 2's:
12 ÷ 2 = 6
, then6 ÷ 2 = 3
- Now
n = 3
- Add
- Increment to
i = 3
- Check condition
3 <= 3 // 3
(3 <= 1): False, exit loop - Check remaining:
n = 3 > 1
, so add3
to set - After processing 12:
s = {2, 3}
Step 3: Process second number (15)
n = 15
, start withi = 2
- Check if
2
divides15
: No (15 % 2 ≠ 0) - Increment to
i = 3
- Check if
3
divides15
: Yes (15 % 3 == 0)- Add
3
to set:s = {2, 3}
(3 already exists, no change) - Remove all 3's:
15 ÷ 3 = 5
- Now
n = 5
- Add
- Increment to
i = 4
- Check condition
4 <= 5 // 4
(4 <= 1): False, exit loop - Check remaining:
n = 5 > 1
, so add5
to set - After processing 15:
s = {2, 3, 5}
Step 4: Process third number (10)
n = 10
, start withi = 2
- Check if
2
divides10
: Yes (10 % 2 == 0)- Add
2
to set:s = {2, 3, 5}
(2 already exists, no change) - Remove all 2's:
10 ÷ 2 = 5
- Now
n = 5
- Add
- Increment to
i = 3
- Check condition
3 <= 5 // 3
(3 <= 1): False, exit loop - Check remaining:
n = 5 > 1
, so add5
to set - After processing 10:
s = {2, 3, 5}
(5 already exists, no change)
Step 5: Return result
- Final set:
s = {2, 3, 5}
- Return
len(s) = 3
The answer is 3 distinct prime factors. We can verify: the product 12 × 15 × 10 = 1800 = 2³ × 3² × 5², which indeed has exactly three distinct prime factors: 2, 3, and 5.
Solution Implementation
1class Solution:
2 def distinctPrimeFactors(self, nums: List[int]) -> int:
3 # Set to store unique prime factors across all numbers
4 prime_factors = set()
5
6 # Process each number in the input array
7 for num in nums:
8 # Start checking for prime factors from 2
9 divisor = 2
10
11 # Check divisors up to sqrt(num) for efficiency
12 while divisor * divisor <= num:
13 # If divisor is a factor of num
14 if num % divisor == 0:
15 # Add this prime factor to the set
16 prime_factors.add(divisor)
17
18 # Remove all occurrences of this prime factor from num
19 while num % divisor == 0:
20 num //= divisor
21
22 # Move to next potential divisor
23 divisor += 1
24
25 # If num > 1 after factorization, it's a prime factor itself
26 if num > 1:
27 prime_factors.add(num)
28
29 # Return the count of distinct prime factors
30 return len(prime_factors)
31
1class Solution {
2 /**
3 * Returns the number of distinct prime factors across all elements in the array.
4 * @param nums - array of positive integers
5 * @return count of unique prime factors found in all array elements
6 */
7 public int distinctPrimeFactors(int[] nums) {
8 // Set to store unique prime factors across all numbers
9 Set<Integer> primeFactors = new HashSet<>();
10
11 // Process each number in the array
12 for (int number : nums) {
13 // Find prime factors of current number
14 findPrimeFactors(number, primeFactors);
15 }
16
17 // Return the count of distinct prime factors
18 return primeFactors.size();
19 }
20
21 /**
22 * Helper method to find and add prime factors of a number to the set.
23 * @param number - the number to factorize
24 * @param primeFactors - set to store the prime factors
25 */
26 private void findPrimeFactors(int number, Set<Integer> primeFactors) {
27 // Check for prime factors from 2 up to sqrt(number)
28 for (int factor = 2; factor * factor <= number; factor++) {
29 // If factor divides the number, it's a prime factor
30 if (number % factor == 0) {
31 // Add the prime factor to the set
32 primeFactors.add(factor);
33
34 // Remove all occurrences of this prime factor
35 while (number % factor == 0) {
36 number /= factor;
37 }
38 }
39 }
40
41 // If number > 1 after factorization, it's a prime factor itself
42 if (number > 1) {
43 primeFactors.add(number);
44 }
45 }
46}
47
1class Solution {
2public:
3 int distinctPrimeFactors(vector<int>& nums) {
4 // Set to store unique prime factors across all numbers
5 unordered_set<int> primeFactors;
6
7 // Process each number in the input array
8 for (int& num : nums) {
9 // Find all prime factors of current number
10 // Only check divisors up to sqrt(num) for efficiency
11 for (int divisor = 2; divisor <= num / divisor; ++divisor) {
12 // Check if divisor is a factor of num
13 if (num % divisor == 0) {
14 // Add this prime factor to the set
15 primeFactors.insert(divisor);
16
17 // Remove all occurrences of this prime factor from num
18 while (num % divisor == 0) {
19 num /= divisor;
20 }
21 }
22 }
23
24 // If num > 1 after factorization, it's a prime factor itself
25 // (This handles prime factors greater than sqrt(original num))
26 if (num > 1) {
27 primeFactors.insert(num);
28 }
29 }
30
31 // Return the count of distinct prime factors
32 return primeFactors.size();
33 }
34};
35
1/**
2 * Counts the number of distinct prime factors across all numbers in the array
3 * @param nums - Array of positive integers
4 * @returns The count of distinct prime factors
5 */
6function distinctPrimeFactors(nums: number[]): number {
7 // Set to store unique prime factors
8 const primeFactors: Set<number> = new Set<number>();
9
10 // Process each number in the array
11 for (let num of nums) {
12 let currentNumber = num;
13 let divisor = 2;
14
15 // Check for prime factors up to sqrt(currentNumber)
16 while (divisor <= currentNumber / divisor) {
17 // If divisor is a factor of currentNumber
18 if (currentNumber % divisor === 0) {
19 // Add the prime factor to the set
20 primeFactors.add(divisor);
21
22 // Remove all occurrences of this prime factor
23 while (currentNumber % divisor === 0) {
24 currentNumber = Math.floor(currentNumber / divisor);
25 }
26 }
27 divisor++;
28 }
29
30 // If currentNumber > 1, it's a prime factor itself
31 if (currentNumber > 1) {
32 primeFactors.add(currentNumber);
33 }
34 }
35
36 // Return the count of distinct prime factors
37 return primeFactors.size;
38}
39
Time and Space Complexity
Time Complexity: O(n × √m)
The algorithm iterates through each of the n
numbers in the array. For each number, it performs prime factorization using trial division. The key optimization is the condition i <= n // i
(equivalent to i² <= n
), which means we only check divisors up to √n
. In the worst case, when factorizing the maximum value m
in the array, we check up to √m
potential divisors. Therefore, the overall time complexity is O(n × √m)
.
Space Complexity: O(m / log m)
The space is primarily used by the set s
to store distinct prime factors. According to the Prime Number Theorem, the number of primes less than or equal to m
is approximately m / ln(m)
. Since we're storing only the distinct prime factors from all numbers in the array, and these prime factors cannot exceed m
, the maximum number of distinct primes we can store is bounded by O(m / log m)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Not Handling Large Prime Factors Correctly
A critical pitfall occurs when forgetting to check if num > 1
after the factorization loop. This remaining value represents a prime factor larger than √num.
Incorrect Implementation:
for num in nums: divisor = 2 while divisor * divisor <= num: if num % divisor == 0: prime_factors.add(divisor) while num % divisor == 0: num //= divisor divisor += 1 # Missing: if num > 1: prime_factors.add(num)
Problem: This misses prime factors greater than √num. For example, with num = 14:
- After dividing by 2, we get 7
- Since 3² = 9 > 7, the loop ends
- We never add 7 to our prime factors set
Solution: Always check and add the remaining value if it's greater than 1.
2. Inefficient Prime Checking with divisor++
Using divisor += 1
checks all numbers, including even numbers after 2, which wastes computation.
Inefficient Approach:
divisor = 2 while divisor * divisor <= num: # ... factorization logic ... divisor += 1 # Checks 2, 3, 4, 5, 6, 7, 8...
Optimized Solution:
divisor = 2 while divisor * divisor <= num: if num % divisor == 0: prime_factors.add(divisor) while num % divisor == 0: num //= divisor # Skip even numbers after 2 if divisor == 2: divisor = 3 else: divisor += 2 # Only check odd numbers: 3, 5, 7, 9...
3. Not Removing All Occurrences of a Prime Factor
Failing to completely divide out a prime factor can lead to treating composite numbers as primes.
Incorrect Implementation:
if num % divisor == 0: prime_factors.add(divisor) num //= divisor # Only divides once! divisor += 1
Problem: For num = 12 (which is 2² × 3):
- After dividing by 2 once, we get 6
- We then check if 3 divides 6, but 6 still contains factor 2
- This could lead to incorrect identification of prime factors
Solution: Use a while loop to completely remove each prime factor:
while num % divisor == 0: num //= divisor
4. Integer Overflow in Condition Check
Using divisor * divisor <= num
can cause integer overflow for very large numbers in some languages.
Safer Alternative:
while divisor <= num // divisor: # Mathematically equivalent but avoids overflow # ... factorization logic ...
This prevents potential overflow issues when divisor
is large, though Python handles big integers naturally.
Consider the classic dynamic programming of longest increasing subsequence:
Find the length of the longest subsequence of a given sequence such that all elements of the subsequence are sorted in increasing order.
For example, the length of LIS for [50, 3, 10, 7, 40, 80]
is 4
and LIS is
[3, 7, 40, 80]
.
What is the recurrence relation?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!