Number of Squareful Arrays - DP w/ Bitmasks [JS]

Description 

Solution: DP w/ Bitmasks

Memoize each dp(mask, prevNum), where

  • mask = bitmask of numbers we have taken in nums
  • prevNum is the last number in the current permuation

For each dp(mask, prevNum),

  • Go through every nums[i] where prevNum + nums[i] is a perfect square
  • Count the number of permuations that are successful

How to handle duplicate permutations:
When we add a new number to the sequence, use a set to keep track of which numbers we have used.
For e.g: The current sequence is [5], the remaining numbers left are [2,2]. We don't want to take [5,2] at this level more than once.

Time Complexity: O(2^n * n * n) 67ms
Space Complexity: O(2^n * n) 41.9MB

var numSquarefulPerms = function(nums) {
  let n = nums.length, allUsed = (1 << n) - 1;
  let memo = new Map(), res = 0, used = new Set();
  for (let i = 0; i < n; i++) {
    if (used.has(nums[i])) continue;
    res += dp(1 << i, nums[i]);
    used.add(nums[i]);
  }
  return res;
  
  function dp(mask, prevNum) {
    if (mask === allUsed) return 1;
    let key = `${mask},${prevNum}`;
    if (memo.has(key)) return memo.get(key);
    
    let ans = 0, used = new Set();
    for (let i = 0; i < n; i++) {
      if ((mask >> i) & 1) continue; // have already used nums[i]
      if (used.has(nums[i])) continue; // already used this number at this level
      if (!isPerfectSquare(prevNum + nums[i])) continue; 
      let newMask = mask | (1 << i);
      ans += dp(newMask, nums[i]);
      used.add(nums[i]);
    }
    memo.set(key, ans);
    return ans;
  }
};

Comments

Popular posts from this blog

Maximum Value of an Ordered Triplet II - Two Solutions [JS]

Maximum Sum of Distinct Subarrays With Length K - Sliding Window w/ Two Pointers & Set [JS]

Sum of Prefix Scores of Strings - Trie [JS]

Maximum Count of Positive Integer and Negative Integer - Binary Search [JS]

Count Subarrays With Median K - Count Left & Right Balance [JS]