3566. Partition Array into Two Equal Product Subsets
Problem Description
You are given an array nums
containing distinct positive integers and an integer target
.
Your task is to determine if you can split the array into two non-empty groups where:
- Every element from
nums
must belong to exactly one group - The two groups cannot share any elements (disjoint)
- The product of all elements in the first group equals
target
- The product of all elements in the second group also equals
target
Return true
if such a partition is possible, otherwise return false
.
Example walkthrough:
- If
nums = [2, 3, 4, 6]
andtarget = 12
- One possible partition: Group 1 =
[2, 6]
with product = 2 Γ 6 = 12, Group 2 =[3, 4]
with product = 3 Γ 4 = 12 - Since both groups have product equal to 12 (the target), return
true
Key constraints:
- All numbers in
nums
are distinct and positive - Both groups must be non-empty (each must contain at least one element)
- Each element must be used exactly once
The solution uses binary enumeration to explore all possible ways to partition the array. For each partition configuration represented by a binary number i
, elements are assigned to either the first group (if the corresponding bit is 1) or the second group (if the bit is 0). The products of both groups are calculated and checked against the target value.
Intuition
Since we need to partition the array into two groups where both groups have the same product equal to target
, we need to explore all possible ways to divide the elements between the two groups.
Think of it this way: for each element in the array, we have a binary choice - it either goes into group 1 or group 2. This naturally leads us to think about binary representation, where each bit position corresponds to an element's group assignment.
For an array of size n
, there are 2^n
total ways to assign elements to two groups. We can represent each partition using a number from 0
to 2^n - 1
. In the binary representation of this number:
- If bit
j
is1
, elementnums[j]
goes to group 1 - If bit
j
is0
, elementnums[j]
goes to group 2
For example, with 4 elements, the number 5
(binary: 0101
) means elements at indices 0 and 2 go to group 1, while elements at indices 1 and 3 go to group 2.
Why does this brute force approach work here? The key insight is that:
- The array size is typically small enough for
2^n
iterations to be feasible - We need to check a specific condition (both products equal to target) rather than optimize something
- There's no obvious greedy strategy or dynamic programming state that would help us avoid checking all possibilities
The beauty of this approach is its simplicity - we systematically check every possible partition using bit manipulation (i >> j & 1
checks if bit j
is set in number i
), calculate the products of both groups, and return true
as soon as we find a valid partition where both products equal the target.
Learn more about Recursion patterns.
Solution Approach
The solution uses binary enumeration to systematically check all possible subset partitions. Here's how the implementation works:
1. Iteration through all partitions:
We iterate through all numbers from 0
to 2^n - 1
using for i in range(1 << n)
, where 1 << n
equals 2^n
. Each value of i
represents a unique way to partition the array.
2. Binary representation for partition assignment:
For each partition state i
, we check each bit position j
from 0
to n-1
:
i >> j & 1
extracts thej
-th bit ofi
- If this bit is
1
, we assignnums[j]
to the first subset (multiply withx
) - If this bit is
0
, we assignnums[j]
to the second subset (multiply withy
)
3. Product calculation:
We initialize both products x
and y
to 1
. As we iterate through each element:
if i >> j & 1: x *= nums[j] # Element goes to first subset else: y *= nums[j] # Element goes to second subset
4. Validation check: After processing all elements for a given partition, we check if both products equal the target:
if x == target and y == target: return True
5. Edge cases handled automatically:
- Empty subsets are naturally avoided because when
i = 0
(all elements in second subset) ori = 2^n - 1
(all elements in first subset), one product will be1
while the other will be the product of all elements - These cases will only return
true
iftarget = 1
and the other product also equals1
Time Complexity: O(n Γ 2^n)
- We check 2^n
partitions and for each partition, we iterate through n
elements.
Space Complexity: O(1)
- We only use a constant amount of extra space for variables x
and y
.
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 = [2, 3, 4]
and target = 6
.
We need to check all possible ways to partition these 3 elements into two groups. With 3 elements, we have 2^3 = 8
possible partitions to check.
Let's trace through each iteration:
i = 0 (binary: 000):
- All bits are 0, so all elements go to group 2
- Group 1: empty β product = 1
- Group 2: [2, 3, 4] β product = 24
- Check: 1 β 6 and 24 β 6 β Continue
i = 1 (binary: 001):
- Bit 0 is 1, so nums[0]=2 goes to group 1
- Group 1: [2] β product = 2
- Group 2: [3, 4] β product = 12
- Check: 2 β 6 and 12 β 6 β Continue
i = 2 (binary: 010):
- Bit 1 is 1, so nums[1]=3 goes to group 1
- Group 1: [3] β product = 3
- Group 2: [2, 4] β product = 8
- Check: 3 β 6 and 8 β 6 β Continue
i = 3 (binary: 011):
- Bits 0 and 1 are 1, so nums[0]=2 and nums[1]=3 go to group 1
- Group 1: [2, 3] β product = 6
- Group 2: [4] β product = 4
- Check: 6 = 6 β but 4 β 6 β Continue
i = 4 (binary: 100):
- Bit 2 is 1, so nums[2]=4 goes to group 1
- Group 1: [4] β product = 4
- Group 2: [2, 3] β product = 6
- Check: 4 β 6 but 6 = 6 β Continue
i = 5 (binary: 101):
- Bits 0 and 2 are 1, so nums[0]=2 and nums[2]=4 go to group 1
- Group 1: [2, 4] β product = 8
- Group 2: [3] β product = 3
- Check: 8 β 6 and 3 β 6 β Continue
i = 6 (binary: 110):
- Bits 1 and 2 are 1, so nums[1]=3 and nums[2]=4 go to group 1
- Group 1: [3, 4] β product = 12
- Group 2: [2] β product = 2
- Check: 12 β 6 and 2 β 6 β Continue
i = 7 (binary: 111):
- All bits are 1, so all elements go to group 1
- Group 1: [2, 3, 4] β product = 24
- Group 2: empty β product = 1
- Check: 24 β 6 and 1 β 6 β Continue
After checking all 8 partitions, we found no valid partition where both groups have product equal to 6. Therefore, the function returns false
.
Note how the bit manipulation i >> j & 1
efficiently determines group assignment: for i=5 (binary: 101), checking bit positions gives us 1, 0, 1 from right to left, placing elements [2, 4] in group 1 and [3] in group 2.
Solution Implementation
1from typing import List
2
3class Solution:
4 def checkEqualPartitions(self, nums: List[int], target: int) -> bool:
5 """
6 Check if the array can be partitioned into two subsets where
7 the product of elements in each subset equals the target value.
8
9 Args:
10 nums: List of integers to partition
11 target: Target product value for each partition
12
13 Returns:
14 True if such partition exists, False otherwise
15 """
16 n = len(nums)
17
18 # Iterate through all possible partitions using bit masking
19 # Each bit position represents whether an element belongs to subset 1 or subset 2
20 for partition_mask in range(1 << n): # 2^n possible partitions
21 subset1_product = 1
22 subset2_product = 1
23
24 # Check each element's assignment based on the current partition mask
25 for element_index in range(n):
26 # Check if the j-th bit is set in the partition mask
27 if (partition_mask >> element_index) & 1:
28 # Element belongs to subset 1
29 subset1_product *= nums[element_index]
30 else:
31 # Element belongs to subset 2
32 subset2_product *= nums[element_index]
33
34 # Check if both subsets have products equal to target
35 if subset1_product == target and subset2_product == target:
36 return True
37
38 # No valid partition found
39 return False
40
1class Solution {
2 /**
3 * Checks if the array can be partitioned into two subsets where
4 * the product of elements in each subset equals the target value.
5 *
6 * @param nums the input array of integers
7 * @param target the target product value for each partition
8 * @return true if such partition exists, false otherwise
9 */
10 public boolean checkEqualPartitions(int[] nums, long target) {
11 int arrayLength = nums.length;
12
13 // Iterate through all possible subset combinations using bit masking
14 // Total combinations: 2^n (where n is array length)
15 int totalCombinations = 1 << arrayLength;
16
17 for (int bitmask = 0; bitmask < totalCombinations; bitmask++) {
18 long firstSubsetProduct = 1;
19 long secondSubsetProduct = 1;
20
21 // Check each element's membership based on current bitmask
22 for (int elementIndex = 0; elementIndex < arrayLength; elementIndex++) {
23 // Extract the bit at position elementIndex
24 boolean isInFirstSubset = ((bitmask >> elementIndex) & 1) == 1;
25
26 if (isInFirstSubset) {
27 // Element belongs to first subset
28 firstSubsetProduct *= nums[elementIndex];
29 } else {
30 // Element belongs to second subset
31 secondSubsetProduct *= nums[elementIndex];
32 }
33 }
34
35 // Check if both subsets have products equal to target
36 if (firstSubsetProduct == target && secondSubsetProduct == target) {
37 return true;
38 }
39 }
40
41 // No valid partition found
42 return false;
43 }
44}
45
1class Solution {
2public:
3 bool checkEqualPartitions(vector<int>& nums, long long target) {
4 int n = nums.size();
5
6 // Iterate through all possible subset combinations using bit masking
7 // Each bit position represents whether an element belongs to subset A (1) or subset B (0)
8 for (int mask = 0; mask < (1 << n); ++mask) {
9 long long productA = 1; // Product of elements in subset A
10 long long productB = 1; // Product of elements in subset B
11
12 // Check each element and assign it to subset A or B based on the mask
13 for (int j = 0; j < n; ++j) {
14 // Check if j-th bit is set in mask
15 if ((mask >> j) & 1) {
16 // Element belongs to subset A
17 productA *= nums[j];
18 } else {
19 // Element belongs to subset B
20 productB *= nums[j];
21 }
22
23 // Early termination: if either product exceeds target, this partition won't work
24 if (productA > target || productB > target) {
25 break;
26 }
27 }
28
29 // Check if both subsets have products equal to target
30 if (productA == target && productB == target) {
31 return true;
32 }
33 }
34
35 // No valid partition found
36 return false;
37 }
38};
39
1/**
2 * Checks if an array can be partitioned into two subsets where
3 * the product of elements in each subset equals the target value
4 * @param nums - Array of numbers to partition
5 * @param target - Target product value for each partition
6 * @returns true if valid partition exists, false otherwise
7 */
8function checkEqualPartitions(nums: number[], target: number): boolean {
9 const arrayLength: number = nums.length;
10
11 // Iterate through all possible subset combinations using bit masking
12 // Each bit position represents whether an element belongs to subset 1 (bit=1) or subset 2 (bit=0)
13 const totalCombinations: number = 1 << arrayLength;
14
15 for (let bitmask: number = 0; bitmask < totalCombinations; ++bitmask) {
16 // Initialize products for both subsets
17 let productSubset1: number = 1;
18 let productSubset2: number = 1;
19
20 // Check each element's subset assignment based on current bitmask
21 for (let elementIndex: number = 0; elementIndex < arrayLength; ++elementIndex) {
22 // Check if bit at position elementIndex is set
23 const isInFirstSubset: boolean = ((bitmask >> elementIndex) & 1) === 1;
24
25 if (isInFirstSubset) {
26 // Element belongs to first subset
27 productSubset1 *= nums[elementIndex];
28 } else {
29 // Element belongs to second subset
30 productSubset2 *= nums[elementIndex];
31 }
32
33 // Early termination: if either product exceeds target, skip this combination
34 if (productSubset1 > target || productSubset2 > target) {
35 break;
36 }
37 }
38
39 // Check if both subsets have the target product
40 if (productSubset1 === target && productSubset2 === target) {
41 return true;
42 }
43 }
44
45 // No valid partition found
46 return false;
47}
48
Time and Space Complexity
Time Complexity: O(2^n Γ n)
The algorithm uses bit manipulation to iterate through all possible partitions of the array into two subsets. The outer loop runs 2^n
times (from 0
to 2^n - 1
), representing all possible subset combinations. For each iteration, the inner loop runs n
times to check each element and decide which subset it belongs to based on the corresponding bit value. Therefore, the total time complexity is O(2^n Γ n)
.
Space Complexity: O(1)
The algorithm only uses a constant amount of extra space. The variables i
, j
, x
, and y
are the only additional variables created, and they don't depend on the input size n
. No additional data structures like arrays or recursion stacks are used, resulting in constant space complexity.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Integer Overflow with Large Products
The most critical pitfall in this solution is that products can grow exponentially large, leading to integer overflow issues. When multiplying many numbers together, the result can exceed standard integer limits, causing incorrect comparisons.
Example scenario:
nums = [100, 200, 300, 400, 500]
- Products can reach values like
100 Γ 200 Γ 300 = 6,000,000
- With more elements, products can easily exceed typical integer bounds
Solution: Instead of computing products directly, use logarithms to convert multiplication to addition:
import math
def checkEqualPartitions(self, nums: List[int], target: int) -> bool:
n = len(nums)
log_target = math.log(target)
for partition_mask in range(1 << n):
log_subset1 = 0
log_subset2 = 0
for element_index in range(n):
if (partition_mask >> element_index) & 1:
log_subset1 += math.log(nums[element_index])
else:
log_subset2 += math.log(nums[element_index])
# Use epsilon for floating-point comparison
epsilon = 1e-9
if abs(log_subset1 - log_target) < epsilon and abs(log_subset2 - log_target) < epsilon:
return True
return False
2. Early Termination Optimization Missing
The current solution always checks all 2^n
partitions even when an early partition might already exceed the target. This wastes computational resources.
Solution: Add early termination when a product exceeds the target:
for partition_mask in range(1 << n):
subset1_product = 1
subset2_product = 1
for element_index in range(n):
if (partition_mask >> element_index) & 1:
subset1_product *= nums[element_index]
if subset1_product > target: # Early termination
break
else:
subset2_product *= nums[element_index]
if subset2_product > target: # Early termination
break
if subset1_product == target and subset2_product == target:
return True
3. Inefficient for Special Cases
The solution doesn't handle special cases efficiently, such as when the target cannot be formed by any subset due to prime factorization constraints.
Solution: Pre-check if the target can be factorized using the given numbers:
def checkEqualPartitions(self, nums: List[int], target: int) -> bool:
# First check if target^2 can be formed by the product of all numbers
total_product = 1
for num in nums:
total_product *= num
if total_product != target * target:
return False # Impossible to form two groups with product = target
# Continue with the original algorithm...
Which of the following uses divide and conquer strategy?
Recommended Readings
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
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!