Taro Logo

Find the Number of Good Pairs II

#859 Most AskedMedium
11 views
Topics:
Arrays

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 <= 105
  • 1 <= nums1[i], nums2[j] <= 106
  • 1 <= k <= 103

Solution


Clarifying Questions

When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:

  1. What is the range of values within the input array, and can the array contain negative numbers or zero?
  2. What should I return if no 'good pairs' are found? Should I return -1, null, or throw an exception?
  3. Are the elements in the array guaranteed to be integers, or could they be floating-point numbers?
  4. Are duplicate numbers allowed within the input array, and if so, how should they be handled when determining 'good pairs'?
  5. Can I modify the input array, or should I treat it as immutable?

Brute Force Solution

Approach

The brute force method for finding good pairs is all about trying every possible pair of numbers. We will look at each number one by one, and for each of those numbers, we'll compare it against every other number in the entire list.

Here's how the algorithm would work step-by-step:

  1. Take the first number in the list.
  2. Compare that first number to every other number in the list, including itself.
  3. Each time the two numbers being compared are equal, count that as a 'good pair'.
  4. Once the first number has been compared to all other numbers, move to the second number in the list.
  5. Compare the second number to every other number in the list, including itself.
  6. Again, each time the two numbers being compared are equal, count that as a 'good pair'.
  7. Repeat this process for every number in the list.
  8. After comparing all numbers to all other numbers, add up the number of 'good pairs' that were found.

Code Implementation

def find_num_good_pairs_brute_force(numbers):
    number_of_good_pairs = 0

    # Iterate through each number in the list
    for first_index in range(len(numbers)):

        # Compare the current number with all other numbers
        for second_index in range(len(numbers)):

            # Check if the numbers at both indices are equal
            if numbers[first_index] == numbers[second_index]:

                # Count the pair if numbers are equal
                number_of_good_pairs += 1

    # Return total number of good pairs
    return number_of_good_pairs

Big(O) Analysis

Time Complexity
O(n²)The given brute force approach iterates through the array of n elements. For each element, it compares it with every other element in the array, resulting in a nested loop structure. Therefore, for each of the n elements, we perform n comparisons. This results in approximately n * n total operations. Thus, the time complexity is O(n²).
Space Complexity
O(1)The brute force algorithm, as described, iterates through the input list and compares each element with every other element. No additional data structures, such as auxiliary arrays or hash maps, are created to store intermediate results or visited elements. It only uses a few constant space variables like loop counters for indexing. Therefore, the auxiliary space used is constant, irrespective of the input size N, where N is the number of elements in the list.

Optimal Solution

Approach

The goal is to efficiently count pairs of numbers that are considered 'good' based on a given difference. To avoid repeatedly checking the same pairs, we'll keep track of how many times each number appears as we go through the list, updating the count as we find matching pairs.

Here's how the algorithm would work step-by-step:

  1. Start by looking at each number in the list, one at a time.
  2. For each number, calculate what other number would make a 'good' pair based on the required difference.
  3. Check if you've seen that 'matching' number before. If you have, add the number of times you've seen it to your running total of 'good' pairs.
  4. Update your record to note that you've now seen the current number once more.
  5. Move on to the next number in the list and repeat the process until you've checked every number.
  6. The final total is the number of 'good' pairs in the list.

Code Implementation

def find_number_of_good_pairs(numbers, difference):
    good_pair_count = 0
    number_frequency = {}

    for number in numbers:
        matching_number = number - difference

        # Check if the matching number exists in the dictionary
        if matching_number in number_frequency:
            good_pair_count += number_frequency[matching_number]

        # Update the frequency of the current number
        if number in number_frequency:
            number_frequency[number] += 1

        #Initialize the frequency if it is not there
        else:
            number_frequency[number] = 1

    return good_pair_count

Big(O) Analysis

Time Complexity
O(n)We iterate through the input list of n numbers once. Inside the loop, we perform a constant-time calculation to find the matching number and a constant-time lookup in a hash map (or dictionary) to check its frequency. We also update the frequency count in the hash map in constant time. Therefore, the dominant operation is the single loop through the input list, making the time complexity O(n).
Space Complexity
O(N)The algorithm maintains a record (likely a hash map or dictionary) to store the counts of each number encountered. In the worst case, all N numbers in the input list are unique, leading to N entries in the record. Therefore, the auxiliary space used to store the counts scales linearly with the input size N. Consequently, the space complexity is O(N).

Edge Cases

Empty array
How to Handle:
Return 0 if the input array is empty, as no pairs can be formed.
Array with one element
How to Handle:
Return 0 if the input array contains only one element, as a pair needs at least two elements.
Array with two identical elements
How to Handle:
This is a valid pair and should be counted as one.
Array with all identical elements
How to Handle:
The solution should correctly count all valid pairs formed by any two distinct indices.
Array with large number of elements
How to Handle:
Ensure that the chosen solution's time and space complexity are efficient enough to handle the input size.
Array containing negative numbers
How to Handle:
The problem definition should specify whether negative numbers are allowed and the solution must correctly handle negative input values.
Array with duplicate numbers and only a few good pairs
How to Handle:
The algorithm should correctly identify and count only the 'good pairs', even when duplicates are present.
Integer overflow when multiplying/adding large numbers
How to Handle:
Use appropriate data types (e.g., long) to prevent potential integer overflow during calculations.
0/1037 completed