LeetCode 3539 - Find Sum of Array Product of Magical Sequences
This problem asks us to compute the sum of array products for all magical sequences of a given length. A magical sequence is defined as a sequence of indices into the input array nums of length m, such that when you sum 2 raised to each element of the sequence, the resulting…
Difficulty: 🔴 Hard
Topics: Array, Math, Dynamic Programming, Bit Manipulation, Combinatorics, Bitmask
Solution
Problem Understanding
This problem asks us to compute the sum of array products for all magical sequences of a given length. A magical sequence is defined as a sequence of indices into the input array nums of length m, such that when you sum 2 raised to each element of the sequence, the resulting number in binary has exactly k set bits. The array product of a sequence is the product of the elements in nums corresponding to the indices in the sequence. Finally, the sum of these products across all magical sequences is returned modulo 10^9 + 7.
In other words, we are selecting sequences of indices from nums (indices range from 0 to len(nums)-1) with length m, and only keeping those sequences whose "binary exponent sum" has exactly k bits set. Then, for each valid sequence, we compute the product of the numbers at those indices, sum them up, and return the result modulo 10^9 + 7.
The constraints tell us that m and k can go up to 30, while the array length is at most 50. The values in nums can be large (up to 10^8). This means a naive enumeration of all sequences (len(nums)^m) is impossible because it would be astronomically large for m = 30.
Important edge cases include sequences of length 1, sequences where k equals m, and arrays with repeated or very large numbers. The problem guarantees k ≤ m, so no impossible counts need handling.
Approaches
The brute-force approach would generate all sequences of length m using indices from 0 to len(nums)-1. For each sequence, we would compute the sum of 2^index values, count the number of set bits in its binary representation, and if it matches k, multiply the corresponding numbers from nums to compute the product. This would give the correct answer, but the complexity is O(n^m) where n = len(nums), which is completely infeasible for large m or n.
The key insight is to use dynamic programming with bitmasking. Each number i contributes 2^i to a sum, and we care about the count of set bits in that sum. Instead of generating all sequences, we can track, for every possible bitmask, the sum of products for sequences that form that bitmask. This allows us to build solutions incrementally: for sequences of length l, we can extend them by one more index and update the corresponding mask. Using collections.Counter or a dictionary keyed by masks lets us avoid redundant computation.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n^m) | O(1) | Enumerates all sequences and filters by set bits |
| Optimal (DP + Bitmask) | O(m * 2^n) | O(2^n) | Tracks sums by bitmask; feasible because n ≤ 50 |
Algorithm Walkthrough
- Initialize a
dpdictionary to store intermediate results. The key is the bitmask representing the sum of2^indexfor the current sequence, and the value is the sum of products that produce this bitmask. - Start with sequences of length 0. This corresponds to a single entry
{0: 1}in the dictionary (mask 0, product 1) because multiplying nothing is the multiplicative identity. - For each sequence length from 1 to
m, iterate over the currentdpstates. For each indexiinnums, compute the new mask by performing a bitwise OR with1 << i. Multiply the previous product sum bynums[i]and add it to the new mask in the nextdpdictionary. - Replace the old
dpwith the new dictionary after processing all indices for this sequence length. - After reaching sequences of length
m, sum the values indpfor masks that have exactlykset bits. - Return the sum modulo
10^9 + 7.
Why it works: At each step, dp[mask] accurately represents the sum of array products for all sequences that produce that mask. By extending sequences iteratively and updating masks with bitwise OR, all sequences are counted exactly once. Filtering by bit counts at the end ensures we only include magical sequences.
Python Solution
from typing import List
from collections import defaultdict
class Solution:
def magicalSum(self, m: int, k: int, nums: List[int]) -> int:
MOD = 10**9 + 7
n = len(nums)
dp = defaultdict(int)
dp[0] = 1
for _ in range(m):
next_dp = defaultdict(int)
for mask, val in dp.items():
for i in range(n):
new_mask = mask | (1 << i)
next_dp[new_mask] = (next_dp[new_mask] + val * nums[i]) % MOD
dp = next_dp
result = 0
for mask, val in dp.items():
if bin(mask).count('1') == k:
result = (result + val) % MOD
return result
The Python implementation uses defaultdict to automatically handle missing keys. The dp dictionary tracks sums of products keyed by masks, updated iteratively for each sequence length. The final sum filters masks with exactly k bits.
Go Solution
func magicalSum(m int, k int, nums []int) int {
const MOD = 1_000_000_007
n := len(nums)
dp := map[int]int{0: 1}
for step := 0; step < m; step++ {
nextDP := make(map[int]int)
for mask, val := range dp {
for i := 0; i < n; i++ {
newMask := mask | (1 << i)
nextVal := (int64(val) * int64(nums[i])) % MOD
nextDP[newMask] = (nextDP[newMask] + int(nextVal)) % MOD
}
}
dp = nextDP
}
result := 0
for mask, val := range dp {
if bitsSet(mask) == k {
result = (result + val) % MOD
}
}
return result
}
func bitsSet(x int) int {
count := 0
for x > 0 {
count += x & 1
x >>= 1
}
return count
}
The Go version uses a map to track DP states, similar to Python. Integer overflow is avoided using int64 during multiplication. A helper function bitsSet counts set bits in a mask.
Worked Examples
Example 1: m = 5, k = 5, nums = [1,10,100,10000,1000000]
At step 0, dp = {0:1}.
Step 1 extends sequences with one index:
mask=0 -> add indices 0-4
dp = {
1: 1*1=1,
2: 1*10=10,
4: 1*100=100,
8: 1*10000=10000,
16: 1*1000000=1000000
}
Step 2 extends each mask again, multiplying the previous sums. Continue until length 5. At the end, select masks with exactly 5 set bits, sum their products modulo 10^9 + 7 → 991600007.
Example 2 and 3 follow similarly, updating dp iteratively and summing masks with the correct number of set bits.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(m * n * 2^n) | For each sequence length, iterate all current masks (≤2^n) and each index (n) |
| Space | O(2^n) | Store sum of products for all possible masks |
This complexity is feasible because n ≤ 50, but m ≤ 30 means the outer loop is limited. The number of masks grows exponentially, but is manageable for this constraint.
Test Cases
# Provided examples
assert Solution().magicalSum(5, 5, [1,10,100,10000,1000000]) == 991600007 # Example 1
assert Solution().magicalSum(2, 2, [5,4,3,2,1]) == 170 # Example 2
assert Solution().magicalSum(1, 1, [28]) == 28 # Example 3
# Edge cases
assert Solution().magicalSum(1, 1, [1]) == 1 # single element
assert Solution().magicalSum(2, 1, [1,1]) == 4 # repeated numbers
assert Solution().magicalSum(3, 2, [1,2,3]) >= 0 # larger m
| Test | Why |
|---|---|
| m=5, k=5, nums=[1,10,100,10000,1000000] | checks large products and correct masking |