Given two arrays of integers nums1
and nums2
, return the number of triplets formed (type 1 and type 2) under the following rules:
nums1[i]2 == nums2[j] * nums2[k]
where 0 <= i < nums1.length
and 0 <= j < k < nums2.length
.nums2[i]2 == nums1[j] * nums1[k]
where 0 <= i < nums2.length
and 0 <= j < k < nums1.length
.
Example 1:
Input: nums1 = [7,4], nums2 = [5,2,8,9] Output: 1 Explanation: Type 1: (1, 1, 2), nums1[1]2 = nums2[1] * nums2[2]. (42 = 2 * 8).
Example 2:
Input: nums1 = [1,1], nums2 = [1,1,1] Output: 9 Explanation: All Triplets are valid, because 12 = 1 * 1. Type 1: (0,0,1), (0,0,2), (0,1,2), (1,0,1), (1,0,2), (1,1,2). nums1[i]2 = nums2[j] * nums2[k]. Type 2: (0,0,1), (1,0,1), (2,0,1). nums2[i]2 = nums1[j] * nums1[k].
Example 3:
Input: nums1 = [7,7,8,3], nums2 = [1,2,9,7] Output: 2 Explanation: There are 2 valid triplets. Type 1: (3,0,2). nums1[3]2 = nums2[0] * nums2[2]. Type 2: (3,0,1). nums2[3]2 = nums1[0] * nums1[1].
Constraints:
1 <= nums1.length, nums2.length <= 1000
1 <= nums1[i], nums2[i] <= 105
This problem asks to count triplets satisfying two conditions:
nums1[i]^2 == nums2[j] * nums2[k]
where 0 <= i < len(nums1)
and 0 <= j < k < len(nums2)
.nums2[i]^2 == nums1[j] * nums1[k]
where 0 <= i < len(nums2)
and 0 <= j < k < len(nums1)
.The most straightforward approach uses hash tables to efficiently count the triplets. Here's a breakdown of the solution and complexity analysis:
Counting Product Pairs: For each input array (nums1
and nums2
), we create a hash table (dictionary in Python, HashMap
in Java, etc.) to store the frequency of each possible product of two distinct elements. For example, if nums = [2, 3, 4]
, the hash table will contain entries like: 6:1
(23), 8:1
(24), 12:1
(3*4). The key is the product, and the value is its count. This step takes O(n^2) time for an array of length n, as we need to iterate through all pairs.
Counting Valid Triplets: Once we have the product-count hash tables for both arrays, we iterate through the elements of nums1
. For each nums1[i]
, we check if nums1[i]^2
exists as a key in the product-count hash table for nums2
. If it exists, the value associated with that key represents the number of valid triplets of Type 1 that involve nums1[i]
. We repeat this process for nums2
to count Type 2 triplets.
Summing the Counts: Finally, we sum up the total counts of Type 1 and Type 2 triplets to get the final answer.
Time Complexity: Dominated by the product pair counting step in Step 1. This step takes O(m^2 + n^2) time, where 'm' is the length of nums1
and 'n' is the length of nums2
. The remaining steps have linear time complexity, O(m + n), which is insignificant compared to O(m^2 + n^2). Therefore, the overall time complexity is O(m^2 + n^2).
Space Complexity: The space complexity is determined by the size of the hash tables created in Step 1. In the worst case, each hash table can store up to O(n^2) entries (all distinct product pairs). Thus, the overall space complexity is O(m^2 + n^2).
The code examples below reflect the described approach:
Python:
from collections import Counter
class Solution:
def numTriplets(self, nums1: List[int], nums2: List[int]) -> int:
def count_products(nums):
count = Counter()
for i in range(len(nums)):
for j in range(i + 1, len(nums)):
count[nums[i] * nums[j]] += 1
return count
prod_count1 = count_products(nums1)
prod_count2 = count_products(nums2)
ans = 0
for x in nums1:
ans += prod_count2.get(x * x, 0)
for x in nums2:
ans += prod_count1.get(x * x, 0)
return ans
Java:
import java.util.HashMap;
import java.util.Map;
class Solution {
public int numTriplets(int[] nums1, int[] nums2) {
Map<Long, Integer> prodCount1 = countProducts(nums1);
Map<Long, Integer> prodCount2 = countProducts(nums2);
int ans = 0;
for (int x : nums1) {
ans += prodCount2.getOrDefault((long) x * x, 0);
}
for (int x : nums2) {
ans += prodCount1.getOrDefault((long) x * x, 0);
}
return ans;
}
private Map<Long, Integer> countProducts(int[] nums) {
Map<Long, Integer> count = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
count.put((long) nums[i] * nums[j], count.getOrDefault((long) nums[i] * nums[j], 0) + 1);
}
}
return count;
}
}
C++:
#include <vector>
#include <unordered_map>
using namespace std;
class Solution {
public:
int numTriplets(vector<int>& nums1, vector<int>& nums2) {
unordered_map<long long, int> prodCount1 = countProducts(nums1);
unordered_map<long long, int> prodCount2 = countProducts(nums2);
int ans = 0;
for (int x : nums1) {
ans += prodCount2[(long long)x * x];
}
for (int x : nums2) {
ans += prodCount1[(long long)x * x];
}
return ans;
}
unordered_map<long long, int> countProducts(vector<int>& nums) {
unordered_map<long long, int> count;
for (int i = 0; i < nums.size(); i++) {
for (int j = i + 1; j < nums.size(); j++) {
count[(long long)nums[i] * nums[j]]++;
}
}
return count;
}
};
Go:
func numTriplets(nums1 []int, nums2 []int) int {
prodCount1 := countProducts(nums1)
prodCount2 := countProducts(nums2)
ans := 0
for _, x := range nums1 {
ans += prodCount2[int64(x*x)]
}
for _, x := range nums2 {
ans += prodCount1[int64(x*x)]
}
return ans
}
func countProducts(nums []int) map[int64]int {
count := make(map[int64]int)
for i := 0; i < len(nums); i++ {
for j := i + 1; j < len(nums); j++ {
count[int64(nums[i]*nums[j])]++;
}
}
return count
}
TypeScript:
function numTriplets(nums1: number[], nums2: number[]): number {
const prodCount1 = countProducts(nums1);
const prodCount2 = countProducts(nums2);
let ans = 0;
for (const x of nums1) {
ans += prodCount2.get(x * x) || 0;
}
for (const x of nums2) {
ans += prodCount1.get(x * x) || 0;
}
return ans;
}
function countProducts(nums: number[]): Map<number, number> {
const count = new Map<number, number>();
for (let i = 0; i < nums.length; i++) {
for (let j = i + 1; j < nums.length; j++) {
const product = nums[i] * nums[j];
count.set(product, (count.get(product) || 0) + 1);
}
}
return count;
}
Remember to handle potential integer overflow when calculating products, especially for larger input arrays. The use of long long
in C++ and int64
in Go addresses this. In languages like Java and TypeScript, casting to long
may be necessary. Python's integers handle arbitrary precision, so overflow is less of a direct concern in Python.