Find the Number of Good Pairs II
MedPrompt
You are given 2 integer arrays nums1 and nums2 of lengths n and m respectively. You are also given a positive integer k.
A pair (i, j) is called good if nums1[i] is divisible by nums2[j] * k (0 <= i <= n - 1, 0 <= j <= m - 1).
Return the total number of good pairs.
Example 1:
Input: nums1 = [1,3,4], nums2 = [1,3,4], k = 1
Output: 5
Explanation:
The 5 good pairs are(0, 0), (1, 0), (1, 1), (2, 0), and (2, 2).Example 2:
Input: nums1 = [1,2,4,12], nums2 = [2,4], k = 3
Output: 2
Explanation:
The 2 good pairs are (3, 0) and (3, 1).
Constraints:
1 <= n, m <= 1051 <= nums1[i], nums2[j] <= 1061 <= k <= 103
Approaches
3 approaches with complexity analysis and trade-offs.
To improve upon the brute-force method, we can avoid re-scanning nums2 for every element of nums1. We can pre-process nums2 by storing its elements and their frequencies in a hash map. Then, for each element num1 in nums1, we find all its divisors. For each divisor d, we check if it could be formed by some num2 * k. This is true if d is a multiple of k. If so, we calculate the required num2 value (d/k) and add its frequency from the map to our total count.
Algorithm
- Create a frequency map,
freq2, for all numbers innums2. AHashMapis a good choice for this. - Initialize a counter
goodPairsCountto 0. - Iterate through each number
num1innums1. - For each
num1, find all of its divisors. This can be done by iterating from 1 up tosqrt(num1). - If
idividesnum1, then bothiandnum1 / iare divisors. - For each divisor
dfound:- Check if
dis divisible byk. - If
d % k == 0, calculateval = d / k. - Look up
valinfreq2. If it exists, add its frequencyfreq2.get(val)togoodPairsCount.
- Check if
- Be careful to handle perfect squares (when
i * i == num1) to avoid double-counting. - Return
goodPairsCount.
Walkthrough
The core idea is to change the check from num1 % (num2 * k) == 0 to finding divisors of num1. If d is a divisor of num1, we need to see if d can be represented as num2 * k for some num2 that exists in the nums2 array. This is equivalent to checking if d is divisible by k and if d/k is a number present in nums2.
import java.util.HashMap;import java.util.Map; class Solution { public long numberOfGoodPairs(int[] nums1, int[] nums2, int k) { Map<Integer, Integer> freq2 = new HashMap<>(); for (int num : nums2) { freq2.put(num, freq2.getOrDefault(num, 0) + 1); } long goodPairsCount = 0; for (int num1 : nums1) { for (int d = 1; d * d <= num1; d++) { if (num1 % d == 0) { // d is a divisor if (d % k == 0) { int val = d / k; goodPairsCount += freq2.getOrDefault(val, 0); } // num1 / d is another divisor int d2 = num1 / d; if (d * d != num1) { // Avoid double counting for perfect squares if (d2 % k == 0) { int val = d2 / k; goodPairsCount += freq2.getOrDefault(val, 0); } } } } } return goodPairsCount; }}Complexity
Time
O(m + n * sqrt(max_val)), where `max_val` is the maximum value in `nums1`. Building the map takes O(m). Then, for each of the `n` numbers in `nums1`, we find its divisors in O(sqrt(num)) time. With `n=10^5` and `max_val=10^6`, this is roughly 10<sup>5</sup> * 1000 = 10<sup>8</sup> operations, which is on the edge of timing out.
Space
O(U2), where `U2` is the number of unique elements in `nums2`. In the worst case, all elements are unique, leading to O(m) space complexity for the hash map.
Trade-offs
Pros
Much faster than the naive brute-force approach.
Reduces redundant work by pre-calculating frequencies of numbers in
nums2.
Cons
The time complexity is still high and may result in a 'Time Limit Exceeded' error for the largest constraints.
The performance is dependent on the magnitude of the numbers in
nums1, not just the length of the array.
Solutions
Solution
class Solution {public long numberOfPairs(int[] nums1, int[] nums2, int k) { Map<Integer, Integer> cnt1 = new HashMap<>(); for (int x : nums1) { if (x % k == 0) { cnt1.merge(x / k, 1, Integer : : sum); } } if (cnt1.isEmpty()) { return 0; } Map<Integer, Integer> cnt2 = new HashMap<>(); for (int x : nums2) { cnt2.merge(x, 1, Integer : : sum); } long ans = 0; int mx = Collections.max(cnt1.keySet()); for (var e : cnt2.entrySet()) { int x = e.getKey(), v = e.getValue(); int s = 0; for (int y = x; y <= mx; y += x) { s += cnt1.getOrDefault(y, 0); } ans += 1L * s * v; } return ans; }}Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.