Hard Leetcode Problem: Number of Squareful Arrays

Here is a hard Leetcode problem: https://leetcode.com/problems/number-of-squareful-arrays/description/

Given an array A of non-negative integers, the array is squareful if for every pair of adjacent elements, their sum is a perfect square.
Return the number of permutations of A that are squareful.  Two permutations A1 and A2 differ if and only if there is some index isuch that A1[i] != A2[i].

Example 1:
Input: [1,17,8]
Output: 2
Explanation: 
[1,8,17] and [17,8,1] are the valid permutations.
Example 2:
Input: [2,2,2]
Output: 1

Note:
  1. 1 <= A.length <= 12
  2. 0 <= A[i] <= 1e9

The array length is very small (12), which allows us to think about a brute-force solution, which is what's done on the post, but I'm sure there is a DP solution too.
The brute-force solution goes like this:

  1. Pre-compute a hash table with the A[i]+A[j] that are perfect square. Do this in 144 steps (fast)
  2. Do a DFS but only calling recursively when the condition is satisfied. In other words, don't check the condition after the combination has been created, but rather check it as you're building the permutation
That should do it. Code is below. Cheers, ACC.


public class Solution
{
 public int NumSquarefulPerms(int[] A)
 {
  Hashtable squares = new Hashtable();

  for (int i = 0; i < A.Length; i++)
  {
   for (int j = i + 1; j < A.Length; j++)
   {
    if (!squares.ContainsKey(A[i] + A[j]))
    {
     double sqrt = Math.Sqrt(A[i] + A[j]);
     squares.Add(A[i] + A[j], ((int)sqrt * (int)sqrt) == A[i] + A[j]);
    }
   }
  }

  int num = 0;
  NumSquarefulPerms(A, 0, -1, new Hashtable(), squares, ref num);
  return num;
 }

 private void NumSquarefulPerms(int[] A,
         int index,
         int previousVal,
         Hashtable usedIndex,
         Hashtable squares,
         ref int num)
 {
  if (index >= A.Length)
  {
   num++;
   return;
  }

  Hashtable usedVal = new Hashtable();
  for (int i = 0; i < A.Length; i++)
  {
   if (!usedVal.ContainsKey(A[i]) &&
    !usedIndex.ContainsKey(i) &&
    (index == 0 || (bool)squares[previousVal + A[i]]))
   {
    usedVal.Add(A[i], true);
    usedIndex.Add(i, true);
    NumSquarefulPerms(A, index + 1, A[i], usedIndex, squares, ref num);
    usedIndex.Remove(i);
   }
  }
 }
}

Comments

  1. Thank you for sharing your nice solution. There are so many good ideas in your code, and also the challenge for me to learn as well.

    I worked on the algorithm over 20 hours last week to learn from various posts, I had difficult time to learn. So I spent time to debug and then I understood your solution quickly and I like the solution very much.

    One thing I learn through the debugging is how you come out the idea using usedVal HashTable to avoid counting duplicate arrays in the function NumSquarefulPerms.

    I did some code review, and simplify a few things using hashet intead of hashtable. And also I add some comments to help understand the most challenge part with a test case [2,2,2].


    I wrote one based on your idea, and also I wrote a post in Leetcode discuss with title "C# depth first search and back tracking with explanation".

    Here is the link:
    https://leetcode.com/problems/number-of-squareful-arrays/discuss/245490/C-depth-first-search-and-back-tracking


    Here is another copy of my code learned from your sharing:

    https://gist.github.com/jianminchen/eeebe33dc77131942de08d3fa21b7934

    ReplyDelete
  2. Love how you’re able to understand the concepts and make it better in your code, great job!

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. I think there's a simpler and more elegant solution. Here's a Python implementation. Note that Python dictionaries (HashMap) remember insertion order. The same behavior can be achieved in Java using LinkedHashMap.
    I don't check my GMail, so if you've any comments, email to contact@asarkar.com.

    class Solution:
    def numSquarefulPerms(self, nums: List[int]) -> int:
    return self._loop(None, len(nums), Counter(nums))

    def _loop(self, last: Optional[int], remaining: int, freq_map: MutableMapping[int, int]) -> int:
    if remaining == 0:
    return 1

    count = 0
    for k in [x for x in freq_map.keys() if freq_map[x] > 0]:
    if not last or math.sqrt(last + k).is_integer():
    freq_map[k] -= 1
    count += self._loop(k, remaining - 1, freq_map)
    freq_map[k] += 1

    return count

    ReplyDelete

Post a Comment

Popular posts from this blog

Golang vs. C#: performance characteristics (simple case study)

Claude vs ChatGPT: A Coder's Perspective on LLM Performance

ProjectEuler Problem 719 (some hints, but no spoilers)