Taro Logo

Number of Divisible Triplet Sums

Medium
IBM logo
IBM
0 views
Topics:
Arrays

Given an integer array nums and an integer d, find the number of index triplets (i, j, k) such that 0 <= i < j < k < nums.length, and the sum nums[i] + nums[j] + nums[k] is divisible by d.

Example 1:

Input: nums = [3,3,6,4,7,5], d = 3
Output: 4
Explanation: The following triplets have a sum divisible by 3:
(3, 3, 6): 3 + 3 + 6 = 12 is divisible by 3
(3, 6, 4): 3 + 6 + 4 = 13. It's wrong.
(3, 6, 5): 3 + 6 + 5 = 14. It's wrong.
(3, 3, 6): 3 + 3 + 6 = 12 is divisible by 3. It's duplicated.
(3, 3, 6): 3 + 3 + 6 = 12 is divisible by 3. It's duplicated.
(3, 6, 4): 3 + 6 + 4 = 13. It's wrong.
(3, 4, 5): 3 + 4 + 5 = 12 is divisible by 3
(3, 7, 5): 3 + 7 + 5 = 15 is divisible by 3
(6, 4, 5): 6 + 4 + 5 = 15 is divisible by 3

Example 2:

Input: nums = [4,2,1,5,3], d = 2
Output: 4
Explanation: The following triplets have a sum divisible by 2:
(4, 2, 1): 4 + 2 + 1 = 7 is not divisible by 2.
(4, 2, 5): 4 + 2 + 5 = 11 is not divisible by 2.
(4, 2, 3): 4 + 2 + 3 = 9 is not divisible by 2.
(4, 1, 5): 4 + 1 + 5 = 10 is divisible by 2.
(4, 1, 3): 4 + 1 + 3 = 8 is divisible by 2.
(4, 5, 3): 4 + 5 + 3 = 12 is divisible by 2.
(2, 1, 5): 2 + 1 + 5 = 8 is divisible by 2.
(2, 1, 3): 2 + 1 + 3 = 6 is divisible by 2.
(2, 5, 3): 2 + 5 + 3 = 10 is divisible by 2.
(1, 5, 3): 1 + 5 + 3 = 9 is not divisible by 2.

Constraints:

  • 3 <= nums.length <= 1000
  • 1 <= nums[i] <= 1000
  • 1 <= d <= 100

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 are the constraints on the size of the `nums` array and the value of `k`?
  2. Can the elements in the `nums` array be negative, zero, or only positive integers?
  3. Are duplicate numbers allowed in the `nums` array, and if so, do they affect the uniqueness of the triplets (i.e., do we need to ensure that the indices i, j, and l are unique, or just the values at those indices)?
  4. If no triplets satisfy the condition (nums[i] + nums[j] + nums[l]) % k == 0, what value should I return?
  5. Is the order of the triplets (i, j, l) significant? For instance, is (0, 1, 2) considered different from (0, 2, 1) if nums[0] + nums[1] + nums[2] is divisible by k in both cases?

Brute Force Solution

Approach

The brute force approach to this problem is about checking every possible combination. We will try every possible group of three numbers from the input to see if their sum is divisible by a given number. If it is, we count it; if not, we move on.

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

  1. Take the first number in the list and pair it with the second and third numbers.
  2. Calculate the sum of these three numbers.
  3. Check if the sum is divisible by the specified number.
  4. If the sum is divisible, mark this combination as a success.
  5. Repeat this process by keeping the first number, but changing the second and third to all other possible combinations.
  6. Once all combinations with the first number are checked, move to the second number and do the same thing: combine it with all possible pairs of the remaining numbers.
  7. Continue this process until every possible group of three numbers has been tested.
  8. Finally, count the total number of successful combinations that resulted in a sum divisible by the specified number.

Code Implementation

def number_of_divisible_triplet_sums(numbers, divisor):
    number_of_numbers = len(numbers)
    count_of_divisible_triplets = 0

    for first_index in range(number_of_numbers):
        for second_index in range(first_index + 1, number_of_numbers):
            for third_index in range(second_index + 1, number_of_numbers):
                # Calculate sum of the current triplet
                triplet_sum = numbers[first_index] + numbers[second_index] + numbers[third_index]

                # Check if the sum is divisible by the divisor
                if triplet_sum % divisor == 0:

                    count_of_divisible_triplets += 1
                    # Increment count if triplet sum is divisible

    return count_of_divisible_triplets

Big(O) Analysis

Time Complexity
O(n^3)The provided brute force approach iterates through all possible triplets in the input array of size n. The outermost loop selects the first number, the second loop selects the second number from the remaining numbers, and the innermost loop selects the third number from the numbers after the second. Therefore, for each element, we have nested loops checking (n-1) and then (n-2) elements on average. This leads to approximately n * (n-1) * (n-2) operations, which simplifies to O(n^3).
Space Complexity
O(1)The provided plain English explanation describes a brute-force approach that iterates through the input array to find triplets. The algorithm does not create any auxiliary data structures like arrays, hash maps, or trees. It only uses a constant number of variables for indexing and calculating sums, regardless of the input size N (the number of elements in the list). Therefore, the auxiliary space complexity is O(1).

Optimal Solution

Approach

The key is to avoid checking every possible group of three numbers. We can use the properties of remainders to cleverly count valid combinations, drastically reducing the work.

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

  1. First, for each number in the list, find its remainder when divided by the target number.
  2. Then, count how many numbers have the same remainder.
  3. Next, consider all possible combinations of three remainders that add up to a multiple of the target number (including zero).
  4. For each of those valid combinations of remainders, calculate how many ways you can pick one number each from the groups having those remainders. You might need to consider cases where you pick the same number multiple times.
  5. Finally, add up all the possible ways you calculated in the previous steps. This gives the total count of triplets that sum to a multiple of the target number.

Code Implementation

def number_of_divisible_triplet_sums(number_array, divisor):    array_length = len(number_array)
    remainder_counts = [0] * divisor

    for number in number_array:
        remainder_counts[number % divisor] += 1

    triplet_count = 0

    #Case 1: (0,0,0)
    triplet_count += remainder_counts[0] * (remainder_counts[0] - 1) * (remainder_counts[0] - 2) // 6

    #Case 2: (a,a,a), where (3 * a) % divisor == 0
    for remainder in range(1, divisor):
        if (3 * remainder) % divisor == 0:
            triplet_count += remainder_counts[remainder] * (remainder_counts[remainder] - 1) * (remainder_counts[remainder] - 2) // 6

    #Case 3: (0, a, divisor-a) 
    for remainder in range(1, divisor // 2 + 1):
        triplet_count += remainder_counts[0] * remainder_counts[remainder] * remainder_counts[divisor - remainder]

    #Case 4: (a, a, divisor - 2a)
    for remainder in range(1, divisor):
        if (2 * remainder) < divisor and (divisor - 2 * remainder) != remainder:
            triplet_count += remainder_counts[remainder] * (remainder_counts[remainder] - 1) // 2 * remainder_counts[divisor - 2 * remainder]

    #Case 5: (a, b, divisor - a - b)
    for remainder_a in range(1, divisor):
        for remainder_b in range(remainder_a + 1, divisor):
            remainder_c = divisor - remainder_a - remainder_b

            if remainder_c >= remainder_b + 1:
                triplet_count += remainder_counts[remainder_a] * remainder_counts[remainder_b] * remainder_counts[remainder_c % divisor]

    #The above is because the total number of triplets need to be divisible.
    return triplet_count

Big(O) Analysis

Time Complexity
O(n + k^3)The algorithm first iterates through the input array of size n to calculate the remainder of each element when divided by the target number, contributing O(n). It then counts the occurrences of each remainder. After that, it iterates through all possible combinations of three remainders, where k is the target number used to compute the remainders. Since we're considering triplets of remainders, this has a time complexity of O(k^3) because we check all possible triplets. Therefore, the overall time complexity is O(n + k^3).
Space Complexity
O(K)The algorithm uses a remainder count array to store the frequency of remainders when each number in the input list is divided by the target number. The size of this array is determined by the target number, which we'll call K. Thus, the space required to store remainder counts scales linearly with K. Therefore, the space complexity is O(K).

Edge Cases

CaseHow to Handle
Empty input arrayReturn 0 since no triplets can be formed.
Input array with fewer than 3 elementsReturn 0 since a triplet cannot be formed.
k is 0Handle division by zero by checking for k==0 and returning 0 if nums[i] + nums[j] + nums[l] is never 0, or iterate and count solutions if sum is ever 0.
Large input array (performance considerations)Optimize the solution for O(n^2) or better complexity to handle large inputs efficiently.
Input array with negative numbersThe modulo operator (%) should be handled correctly for negative remainders by adding k if the result is negative.
Input array with zero valuesThe solution should correctly count triplets involving zero values.
Integer overflow in the sum of nums[i] + nums[j] + nums[l]Use a larger data type (e.g., long) to store the sum to prevent integer overflow.
All numbers in the array are the sameCarefully calculate the number of combinations nCr, where n is the size of the array and r is 3, to avoid overcounting.