The Number of Good Subsets - Complete Solution Guide
The Number of Good Subsets is LeetCode problem 1994, a Hard level challenge. This complete guide provides step-by-step explanations, multiple solution approaches, and optimized code in python3, java, cpp, c.
Problem Statement
You are given an integer array nums . We call a subset of nums good if its product can be represented as a product of one or more distinct prime numbers. For example, if nums = [1, 2, 3, 4] : [2, 3] , [1, 2, 3] , and [1, 3] are good subsets with products 6 = 2*3 , 6 = 2*3 , and 3 = 3 respectively. [1, 4] and [4] are not good subsets with products 4 = 2*2 and 4 = 2*2 respectively. Return the number of different good subsets in nums modulo 10 9 + 7 . A subset of nums is any array that can be obtai
Detailed Explanation
The problem asks us to find the number of "good" subsets from a given array of integers. A subset is considered "good" if the product of its elements can be expressed as a product of distinct prime numbers. We need to return the count of such good subsets modulo 10^9 + 7. Note that different subsets formed from the same elements at different indices count as distinct.
Solution Approach
The solution uses dynamic programming with bitmasking. First, it counts the occurrences of each number from 1 to 30. Then, it iterates through the numbers from 2 to 30. For each number, it checks if it is a valid candidate for a 'good' subset (i.e., its prime factorization consists of distinct primes). If it is, a bitmask is created representing the prime factors of the number. The DP table, `dp`, is updated iteratively. `dp[mask]` stores the number of subsets that result in a product with exactly the primes denoted by mask. Finally, the algorithm sums up the values in the `dp` table (excluding `dp[0]`, the empty set), multiplies by the power of 2 representing the number of ways to include 1s, and returns the result modulo 10^9 + 7.
Step-by-Step Algorithm
- Step 1: Initialize `primes` array with prime numbers less than or equal to 30: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29].
- Step 2: Create a `counts` array to store the frequency of each number from 1 to 30 in the input array `nums`.
- Step 3: Initialize a `dp` array of size 2^10 (where 10 is the number of primes) to store the counts of subsets with different prime factor combinations. Set `dp[0] = 1` because the empty set is always valid and its product has no prime factors.
- Step 4: Iterate through the numbers from 2 to 30. For each number `num`, check if it can contribute to a 'good' subset. If `num` is divisible by 4, 9, or 25, skip it, as its product won't be distinct primes.
- Step 5: Create a bitmask representing the prime factors of `num`. For each prime in the `primes` array, if `num` is divisible by the prime, set the corresponding bit in the mask.
- Step 6: Update the `dp` array. Iterate backwards from `(1 << num_primes) - 1` to 0. If the current mask `j` does not have any of the prime factors of `num` (i.e., `(j & mask) == 0`), then update `dp[j | mask]` by adding `dp[j] * counts[num]` (modulo MOD). This represents adding the current number `num` to all subsets represented by `dp[j]` which does not conflict with the `mask` of `num`.
- Step 7: After iterating through all numbers from 2 to 30, calculate the sum of all values in the `dp` array, excluding `dp[0]` (representing the empty set). This gives the number of good subsets excluding '1'.
- Step 8: Calculate 2 raised to the power of the number of 1s in the input array (`counts[1]`) modulo MOD. This represents the number of ways to include 1s in any subset.
- Step 9: Multiply the sum calculated in step 7 by the power of 2 calculated in step 8 (modulo MOD) to account for all possible combinations including 1s. Return the result as an integer.
Key Insights
- Insight 1: The numbers in the input array are constrained to be between 1 and 30 inclusive. This limits the number of possible prime factors to a small, fixed set of primes: 2, 3, 5, 7, 11, 13, 17, 19, 23, and 29. This allows for a bitmask approach using these primes.
- Insight 2: Dynamic programming with bitmasking can be used to efficiently count the number of good subsets. The bitmask represents the primes included in the product of the current subset. `dp[mask]` stores the number of subsets whose product has the prime factors represented by `mask`.
- Insight 3: The number '1' can be included in any good subset without affecting whether it remains a good subset. Therefore, we can count the number of 1s separately and multiply the final result by 2^(number of 1s) to account for all possible combinations including 1s.
- Insight 4: Numbers that are not products of distinct primes (e.g., 4, 9, 25) cannot be part of a good subset, as they introduce repeated prime factors. These should be skipped.
Complexity Analysis
Time Complexity: O(n + 30 * 2^10 + 2^10)
Space Complexity: O(n + 2^10)
Topics
This problem involves: Array, Hash Table, Math, Dynamic Programming, Bit Manipulation, Counting, Number Theory, Bitmask.
Companies
Asked at: Lowe's, Media.net.