Facebook Pixel

1577. Number of Ways Where Square of Number Is Equal to Product of Two Numbers

Problem Description

You are given two arrays of integers nums1 and nums2. Your task is to count the total number of valid triplets that can be formed following two specific rules.

A triplet consists of three indices, and there are two types of valid triplets:

Type 1 Triplet: A triplet (i, j, k) where:

  • i is an index from nums1
  • j and k are indices from nums2 with j < k
  • The relationship nums1[i]² == nums2[j] * nums2[k] must hold

Type 2 Triplet: A triplet (i, j, k) where:

  • i is an index from nums2
  • j and k are indices from nums1 with j < k
  • The relationship nums2[i]² == nums1[j] * nums1[k] must hold

In simpler terms:

  • For Type 1: Take one element from nums1, square it, and check if it equals the product of any two elements from nums2 (where the second element comes after the first)
  • For Type 2: Take one element from nums2, square it, and check if it equals the product of any two elements from nums1 (where the second element comes after the first)

The function should return the total count of all valid triplets (both Type 1 and Type 2 combined).

For example, if nums1 = [7, 4] and nums2 = [5, 2, 8, 9]:

  • A Type 1 triplet could be (1, 0, 2) because nums1[1]² = 16 = nums2[0] * nums2[2] = 2 * 8
  • A Type 2 triplet could be (3, 0, 1) because nums2[3]² = 81 = nums1[0] * nums1[1] = 7 * 4 (this is just an example, not necessarily valid)
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is to recognize that we're essentially looking for matching relationships between squares and products across two arrays. Instead of checking every possible triplet combination which would be inefficient, we can precompute all possible products and then match them with squares.

Think about it this way: for Type 1 triplets, we need to find cases where nums1[i]² = nums2[j] * nums2[k]. Rather than fixing i and then searching for all valid (j, k) pairs, we can flip our perspective:

  1. First, calculate all possible products from pairs in nums2 (where j < k)
  2. Then, for each element in nums1, square it and check how many times this squared value appears in our precomputed products

This transforms the problem from a three-way matching problem into a two-step process:

  • Step 1: Generate all products from pairs within each array
  • Step 2: Match squares from one array with products from the other

The beauty of this approach is that we can use a hash table (Counter) to store the frequency of each product. Why frequency? Because multiple pairs might produce the same product. For instance, 2 * 6 = 12 and 3 * 4 = 12. If we're looking for triplets where some nums1[i]² = 12, we need to count both pairs.

By separating the counting of products from the matching with squares, we avoid redundant calculations. We compute all products once for each array, store them with their frequencies, and then simply look up how many times each squared value appears in the opposite array's product table.

This bidirectional approach (Type 1: squares from nums1 matched with products from nums2, and Type 2: squares from nums2 matched with products from nums1) ensures we capture all valid triplets while maintaining an efficient time complexity.

Learn more about Math and Two Pointers patterns.

Solution Approach

The implementation uses a hash table-based approach with helper functions to modularize the logic:

Step 1: Create a Product Counting Function

The count function takes an array and returns a Counter (hash table) that stores all possible products of pairs (j, k) where j < k:

def count(nums: List[int]) -> Counter:
    cnt = Counter()
    for j in range(len(nums)):
        for k in range(j + 1, len(nums)):
            cnt[nums[j] * nums[k]] += 1
    return cnt

This function iterates through all valid pairs in the array. For each pair, it calculates their product and increments the count in the hash table. The time complexity is O(n²) where n is the length of the array.

Step 2: Create a Calculation Function

The cal function takes an array and a Counter, then calculates how many valid triplets can be formed:

def cal(nums: List[int], cnt: Counter) -> int:
    return sum(cnt[x * x] for x in nums)

For each element x in the array, it squares it (x * x) and looks up how many times this squared value appears in the Counter. The sum of all these counts gives us the total number of valid triplets for this configuration.

Step 3: Apply to Both Types of Triplets

The main solution combines these helper functions:

cnt1 = count(nums1)  # All products from pairs in nums1
cnt2 = count(nums2)  # All products from pairs in nums2
return cal(nums1, cnt2) + cal(nums2, cnt1)
  • cal(nums1, cnt2): Counts Type 2 triplets - squares from nums1 matched with products from nums2
  • cal(nums2, cnt1): Counts Type 1 triplets - squares from nums2 matched with products from nums1

Note the seemingly reversed logic here: cal(nums1, cnt2) actually counts Type 2 triplets because:

  • We're squaring elements from nums1
  • We're matching them with products from nums2
  • This corresponds to nums1[j] * nums1[k] = nums2[i]² (Type 2)

Time Complexity: O(m² + n²) where m and n are the lengths of nums1 and nums2 respectively, due to generating all pairs.

Space Complexity: O(m² + n²) for storing the products in the hash tables.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a small example with nums1 = [7, 4] and nums2 = [5, 2, 8, 9].

Step 1: Count all products from pairs

For nums1 = [7, 4]:

  • Only one pair: (0, 1)7 * 4 = 28
  • cnt1 = {28: 1}

For nums2 = [5, 2, 8, 9]:

  • Pair (0, 1): 5 * 2 = 10
  • Pair (0, 2): 5 * 8 = 40
  • Pair (0, 3): 5 * 9 = 45
  • Pair (1, 2): 2 * 8 = 16
  • Pair (1, 3): 2 * 9 = 18
  • Pair (2, 3): 8 * 9 = 72
  • cnt2 = {10: 1, 40: 1, 45: 1, 16: 1, 18: 1, 72: 1}

Step 2: Match squares with products

For Type 1 triplets (square from nums1, products from nums2):

  • nums1[0]² = 7² = 49: Check if 49 exists in cnt2 → No match
  • nums1[1]² = 4² = 16: Check if 16 exists in cnt2 → Yes! Count = 1
  • This gives us triplet (1, 1, 2) where nums1[1]² = 16 = nums2[1] * nums2[2] = 2 * 8

For Type 2 triplets (square from nums2, products from nums1):

  • nums2[0]² = 5² = 25: Check if 25 exists in cnt1 → No match
  • nums2[1]² = 2² = 4: Check if 4 exists in cnt1 → No match
  • nums2[2]² = 8² = 64: Check if 64 exists in cnt1 → No match
  • nums2[3]² = 9² = 81: Check if 81 exists in cnt1 → No match

Step 3: Sum up all matches

Total valid triplets = 1 (from Type 1) + 0 (from Type 2) = 1

The algorithm efficiently found the single valid triplet (1, 1, 2) by precomputing products and matching them with squares, avoiding the need to check all possible three-element combinations.

Solution Implementation

1class Solution:
2    def numTriplets(self, nums1: List[int], nums2: List[int]) -> int:
3        """
4        Count the number of triplets where:
5        - Type 1: nums1[i]^2 = nums2[j] * nums2[k]
6        - Type 2: nums2[i]^2 = nums1[j] * nums1[k]
7        """
8      
9        def count_products(nums: List[int]) -> Counter:
10            """
11            Count frequency of all possible products from pairs in the array.
12          
13            Args:
14                nums: Input array of integers
15              
16            Returns:
17                Counter object with products as keys and frequencies as values
18            """
19            product_counter = Counter()
20          
21            # Iterate through all unique pairs (j, k) where j < k
22            for j in range(len(nums)):
23                for k in range(j + 1, len(nums)):
24                    product = nums[j] * nums[k]
25                    product_counter[product] += 1
26                  
27            return product_counter
28      
29        def calculate_triplets(nums: List[int], product_counter: Counter) -> int:
30            """
31            Calculate number of valid triplets by checking if square of each number
32            exists in the product counter.
33          
34            Args:
35                nums: Array containing numbers to square
36                product_counter: Counter of products from the other array
37              
38            Returns:
39                Total count of valid triplets
40            """
41            total = 0
42          
43            # For each number, check if its square matches any product
44            for num in nums:
45                square = num * num
46                total += product_counter[square]
47              
48            return total
49      
50        # Count all possible products for both arrays
51        products_nums1 = count_products(nums1)
52        products_nums2 = count_products(nums2)
53      
54        # Calculate Type 1 triplets (nums2[i]^2 = nums1[j] * nums1[k])
55        # and Type 2 triplets (nums1[i]^2 = nums2[j] * nums2[k])
56        type1_triplets = calculate_triplets(nums2, products_nums1)
57        type2_triplets = calculate_triplets(nums1, products_nums2)
58      
59        return type1_triplets + type2_triplets
60
1class Solution {
2    /**
3     * Counts the number of Type 1 and Type 2 triplets.
4     * Type 1: i from nums1, j and k from nums2, where nums1[i]^2 = nums2[j] * nums2[k]
5     * Type 2: i from nums2, j and k from nums1, where nums2[i]^2 = nums1[j] * nums1[k]
6     * 
7     * @param nums1 First array of integers
8     * @param nums2 Second array of integers
9     * @return Total count of Type 1 and Type 2 triplets
10     */
11    public int numTriplets(int[] nums1, int[] nums2) {
12        // Count all possible products of pairs in each array
13        Map<Long, Integer> productCountMap1 = count(nums1);
14        Map<Long, Integer> productCountMap2 = count(nums2);
15      
16        // Calculate Type 1 triplets (square from nums1, pair from nums2)
17        // and Type 2 triplets (square from nums2, pair from nums1)
18        return cal(productCountMap1, nums2) + cal(productCountMap2, nums1);
19    }
20
21    /**
22     * Counts the frequency of all possible products of pairs in the array.
23     * 
24     * @param nums Input array
25     * @return Map where key is the product and value is its frequency
26     */
27    private Map<Long, Integer> count(int[] nums) {
28        Map<Long, Integer> productCount = new HashMap<>();
29        int n = nums.length;
30      
31        // Iterate through all pairs (j, k) where j < k
32        for (int j = 0; j < n; ++j) {
33            for (int k = j + 1; k < n; ++k) {
34                // Calculate product and cast to long to avoid overflow
35                long product = (long) nums[j] * nums[k];
36                // Increment the count for this product
37                productCount.merge(product, 1, Integer::sum);
38            }
39        }
40      
41        return productCount;
42    }
43
44    /**
45     * Calculates the number of valid triplets where the square of elements
46     * from nums matches the products in the count map.
47     * 
48     * @param productCount Map of products and their frequencies
49     * @param nums Array whose elements will be squared
50     * @return Number of valid triplets
51     */
52    private int cal(Map<Long, Integer> productCount, int[] nums) {
53        int tripletCount = 0;
54      
55        // For each number in nums, check if its square exists in the product map
56        for (int num : nums) {
57            long square = (long) num * num;
58            // Add the frequency of this square value to the result
59            tripletCount += productCount.getOrDefault(square, 0);
60        }
61      
62        return tripletCount;
63    }
64}
65
1class Solution {
2public:
3    int numTriplets(vector<int>& nums1, vector<int>& nums2) {
4        // Count all possible products of pairs from each array
5        auto pairProductCount1 = countPairProducts(nums1);
6        auto pairProductCount2 = countPairProducts(nums2);
7      
8        // Calculate Type 1 triplets (nums1[i]^2 = nums2[j] * nums2[k])
9        // and Type 2 triplets (nums2[i]^2 = nums1[j] * nums1[k])
10        return calculateTriplets(pairProductCount1, nums2) + 
11               calculateTriplets(pairProductCount2, nums1);
12    }
13
14private:
15    // Count frequency of all possible products from pairs in the array
16    unordered_map<long long, int> countPairProducts(vector<int>& nums) {
17        unordered_map<long long, int> productFrequency;
18      
19        // Iterate through all unique pairs (i, j) where i < j
20        for (int i = 0; i < nums.size(); i++) {
21            for (int j = i + 1; j < nums.size(); j++) {
22                // Store the product as long long to avoid overflow
23                long long product = static_cast<long long>(nums[i]) * nums[j];
24                productFrequency[product]++;
25            }
26        }
27      
28        return productFrequency;
29    }
30
31    // Calculate number of valid triplets where nums[i]^2 equals a pair product
32    int calculateTriplets(unordered_map<long long, int>& pairProductCount, 
33                          vector<int>& nums) {
34        int tripletCount = 0;
35      
36        // For each number in nums, check if its square exists in pair products
37        for (int num : nums) {
38            long long squaredValue = static_cast<long long>(num) * num;
39          
40            // Add the frequency of this squared value in pair products
41            if (pairProductCount.find(squaredValue) != pairProductCount.end()) {
42                tripletCount += pairProductCount[squaredValue];
43            }
44        }
45      
46        return tripletCount;
47    }
48};
49
1/**
2 * Counts the number of triplets where nums1[i]^2 = nums2[j] * nums2[k] 
3 * and nums2[i]^2 = nums1[j] * nums1[k]
4 * @param nums1 - First array of numbers
5 * @param nums2 - Second array of numbers
6 * @returns Total count of valid triplets
7 */
8function numTriplets(nums1: number[], nums2: number[]): number {
9    // Count all products of pairs in each array
10    const productCount1: Map<number, number> = count(nums1);
11    const productCount2: Map<number, number> = count(nums2);
12  
13    // Calculate triplets where square from nums2 equals product from nums1
14    // and square from nums1 equals product from nums2
15    return cal(productCount1, nums2) + cal(productCount2, nums1);
16}
17
18/**
19 * Creates a frequency map of all products from pairs of elements in the array
20 * @param nums - Input array of numbers
21 * @returns Map where key is product and value is frequency
22 */
23function count(nums: number[]): Map<number, number> {
24    const productFrequencyMap: Map<number, number> = new Map();
25  
26    // Iterate through all pairs (j, k) where j < k
27    for (let j = 0; j < nums.length; ++j) {
28        for (let k = j + 1; k < nums.length; ++k) {
29            const product: number = nums[j] * nums[k];
30            // Increment the frequency count for this product
31            productFrequencyMap.set(product, (productFrequencyMap.get(product) || 0) + 1);
32        }
33    }
34  
35    return productFrequencyMap;
36}
37
38/**
39 * Calculates the number of valid triplets by matching squares with products
40 * @param cnt - Map of products and their frequencies
41 * @param nums - Array to check squares against the products
42 * @returns Count of matching triplets
43 */
44function cal(cnt: Map<number, number>, nums: number[]): number {
45    // For each number, check if its square exists in the product map
46    // and accumulate the frequency count
47    return nums.reduce((accumulator: number, currentValue: number) => {
48        const square: number = currentValue * currentValue;
49        return accumulator + (cnt.get(square) || 0);
50    }, 0);
51}
52

Time and Space Complexity

Time Complexity: O(m^2 + n^2 + m + n)

The time complexity breaks down as follows:

  • The count function is called twice - once for nums1 and once for nums2
    • For nums1: The nested loops iterate through all pairs (j, k) where j < k, resulting in O(m^2) operations
    • For nums2: Similarly, this takes O(n^2) operations
  • The cal function is called twice
    • First call iterates through nums1 and looks up values in cnt2, taking O(m) time
    • Second call iterates through nums2 and looks up values in cnt1, taking O(n) time
  • Total time complexity: O(m^2) + O(n^2) + O(m) + O(n) = O(m^2 + n^2 + m + n)

Space Complexity: O(m^2 + n^2)

The space complexity is determined by:

  • cnt1 stores all possible products of pairs from nums1. In the worst case, all pairs produce unique products, resulting in O(m^2) space
  • cnt2 stores all possible products of pairs from nums2. Similarly, this requires O(n^2) space in the worst case
  • Total space complexity: O(m^2) + O(n^2) = O(m^2 + n^2)

Where m is the length of nums1 and n is the length of nums2.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Confusion About Type 1 vs Type 2 Mapping

The Pitfall: The most common mistake is getting confused about which calculation corresponds to which triplet type. In the code, calculate_triplets(nums2, products_nums1) actually counts Type 1 triplets, not Type 2. This seems counterintuitive because:

  • Type 1 is defined as: nums1[i]² == nums2[j] * nums2[k]
  • But we're calling calculate_triplets(nums2, products_nums1)

Many developers mistakenly think they should square elements from nums1 for Type 1 triplets.

Why This Happens: The confusion arises from misreading the problem statement. For Type 1:

  • We need ONE element from nums1 (at index i)
  • We need TWO elements from nums2 (at indices j and k)
  • So we square the single element and match it against products of pairs

The Solution: Remember the pattern:

  • When you need to match one squared element from array A with products of pairs from array B
  • You compute products from B and square elements from A
  • The array providing the single element gets squared; the array providing pairs gets multiplied

2. Integer Overflow Issues

The Pitfall: When dealing with large integers, squaring them or multiplying pairs can cause overflow issues. For example:

  • If nums1 = [100000] and nums2 = [100000, 1]
  • Computing 100000² = 10,000,000,000 which exceeds 32-bit integer limits

The Solution: In Python, this is handled automatically as integers can be arbitrarily large. However, in other languages like Java or C++, you should:

# Use long/int64 for calculations
square = int(num) * int(num)  # Explicit casting if needed
product = int(nums[j]) * int(nums[k])

3. Counting Duplicate Products Incorrectly

The Pitfall: Not properly handling cases where the same product can be formed by different pairs. For example:

  • nums = [2, 3, 4, 6]
  • Product 12 can be formed by: (2, 6) and (3, 4)
  • Both should be counted separately

The Solution: The Counter approach handles this correctly by incrementing the count for each occurrence:

product_counter[product] += 1  # Correctly accumulates all occurrences

4. Including Invalid Pairs (j >= k)

The Pitfall: Accidentally counting pairs where j >= k or counting the same pair twice (once as (j,k) and once as (k,j)).

Incorrect approach:

# Wrong: This counts each pair twice
for j in range(len(nums)):
    for k in range(len(nums)):
        if j != k:  # Still wrong!
            product_counter[nums[j] * nums[k]] += 1

The Solution: Always ensure j < k in your loop structure:

for j in range(len(nums)):
    for k in range(j + 1, len(nums)):  # Ensures j < k
        product_counter[nums[j] * nums[k]] += 1

5. Edge Cases with Empty or Single-Element Arrays

The Pitfall: Not handling cases where:

  • One or both arrays are empty
  • An array has only one element (can't form pairs)

The Solution: The current implementation handles these gracefully:

  • Empty array → No pairs → Empty Counter → Zero triplets
  • Single element → No valid pairs where j < k → Zero products → Zero triplets

However, you could add explicit checks for clarity:

if len(nums1) < 2 and len(nums2) < 2:
    return 0  # No valid triplets possible
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What is an advantages of top-down dynamic programming vs bottom-up dynamic programming?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More