2198. Number of Single Divisor Triplets
Problem Description
You are tasked with finding the number of special triplets in a given array of positive integers, where the array is indexed starting at 0. A triplet consists of three distinct indices (i, j, k)
. This triplet is considered special - termed as a single divisor triplet - if the sum of the numbers at these indices, specifically nums[i] + nums[j] + nums[k]
, is divisible by exactly one of the three numbers nums[i]
, nums[j]
, or nums[k]
. The goal is to count how many such triplets exist in the provided array.
Intuition
To solve this problem, we should first understand that checking every possible triplet would be quite inefficient if we did so naïvely, since that requires looking at all possible combinations of numbers in the array. However, by utilizing the Counter
class from Python's collections
module, we can improve the efficiency when certain numbers are repeated in nums
.
The solution approach involves the following steps:
- We count the occurrences of each number in the array utilizing the
Counter
class. - Once we have the counts of each number, we iterate through the array thrice – simulating the selection of three distinct elements, denoted by
a
,b
, andc
, for our triplet. Ifa
,b
, andc
are the same number, we skip to avoid checking the same combination again. It's important to note thata
,b
, orc
could be the same number but from different indices. - For each triplet, we check if the sum of
a
,b
, andc
(s = a + b + c
) is divisible by only one of the three numbers. Thecheck
function helps in this regard by returningTrue
if exactly one divisibility condition is met. - If the
check
function returnsTrue
, we must account for the occurrences of each number. Since the numbers might be duplicated innums
, we need to calculate the number of triplets that we can form with the given counts ofa
,b
, andc
. We do this by considering the different scenarios:- If two numbers are the same and the third is different, we use combinations like
cnt1 * (cnt1 - 1) * cnt3
to reflect the number of possible triplets. - If all three numbers are different, we simply multiply the counts:
cnt1 * cnt2 * cnt3
.
- If two numbers are the same and the third is different, we use combinations like
- We sum up these counts to get the total number of single divisor triplets.
Using the Counter
allows us to avoid unnecessary duplicate computations and vastly reduces the number of required operations, especially when dealing with an array containing many repeated numbers.
Learn more about Math patterns.
Solution Approach
The implementation of the solution leverages the Counter
class from Python's collections
module to efficiently track the number of times each number appears in the input array nums
. This is crucial to avoid duplicate computations when numbers are repeated. Here's a step-by-step breakdown of the algorithm:
-
A
Counter
object is created for the arraynums
to get the count of each unique number. For example, ifnums
is[1, 2, 2, 3]
, theCounter
object would be{1: 1, 2: 2, 3: 1}
. -
We define a helper function
check(a, b, c)
which calculates the sums
of the parametersa
,b
, andc
. It then checks ifs
is divisible by exactly one of the valuesa
,b
, orc
by using the modulo operations % x
for each and counts the occurrences of0
(true divisibility) using a generator expression. If this count is1
, it returnsTrue
; otherwise, it returnsFalse
. -
The solution then iterates through every possible combination of the counts of each unique number, looking for ways to form triplets. The outer loop picks a number, denoted as
a
, from theCounter
. The nested loops pick additional numbers,b
andc
. -
For each triplet
(a, b, c)
, it calls thecheck
function to verify whether it's a single divisor triplet. If it is, the count of possible single divisor triplets that can be made witha
,b
, andc
is calculated. This depends on whethera
,b
, andc
are the same or different. For example:- If
a
is the same asb
but different fromc
, the number of triplets iscnt1 * (cnt1 - 1) / 2 * cnt3
because there arecnt1 * (cnt1 - 1) / 2
ways to choose two numbersa
andb
fromcnt1
occurrences, andcnt3
ways to choosec
. - If all three numbers are different, the number of triplets will be
cnt1 * cnt2 * cnt3
since there arecnt1
ways to choosea
,cnt2
ways to chooseb
, andcnt3
ways to choosec
.
- If
-
It's essential to handle overcounting carefully; the solution ensures that each triplet of numbers is counted exactly once, with attention to the distinction between using combinations vs. permutations when selecting elements from counts.
-
As each valid triplet is found, its count is added to an accumulator variable,
ans
, which finally holds the total count of single divisor triplets. -
After checking all possible combinations of numbers from the
Counter
, the final result is returned, which is the accumulated count of all valid triplets.
Thus, the algorithm makes smart use of combinatorics and the properties of divisibility to compute the desired count without exhaustively examining each distinct triplet in the original array.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through an example using the provided solution approach to get a clearer understanding of how the algorithm works.
Given Array
Consider the array nums = [1, 2, 2, 3, 4]
.
Step 1: Counter Object Creation
We create a Counter
object from nums
which gives us {1: 1, 2: 2, 3: 1, 4: 1}
.
Step 2: Define the check
function
The check(a, b, c)
function takes three numbers and returns True
if the sum s = a + b + c
is divisible by exactly one of a
, b
, or c
.
Step 3: Iterate over unique combinations
We start iterating over every unique combination using the counts from the Counter
object:
- First we pick
a = 1
from the counter. - Then we pick
b = 2
. Since we have two 2's,cnt2 = 2
. - Next, we pick
c = 4
. Since there is only one 4,cnt3 = 1
.
Step 4: Apply the check
function and calculate the triplet count
Using the check
function, we find that 1 + 2 + 4 = 7
is not divisible by any number exactly once, so we discard this triplet.
Next, we try a = 2
, b = 2
, and c = 4
(where a
and b
are the same). The sum is 2 + 2 + 4 = 8
, and this sum is divisible by 4
only, which makes it a valid triplet. Since a
and b
are the same, there are cnt2 * (cnt2 - 1) / 2
ways to pick a
and b
, and cnt3
ways to pick c
. This gives us a count of (2 * (2 - 1) / 2) * 1 = 1
.
We continue this process for other possible combinations.
Step 5: Add valid triplets count to the accumulator
Each time we find a valid triplet combination, we increment our accumulator ans
with the count of its occurrences.
Step 6: Final result
After iterating through all unique number combinations from the Counter
, we sum up all valid triplet counts we have found. Suppose we found another valid triplet using different numbers, let's say we found 1
more valid triplet in the process. Our total count ans
would be 1 + 1 = 2
.
Thus, for the given array [1, 2, 2, 3, 4]
, our final result is 2
, meaning there are two single divisor triplets whose sum is divisible exactly by one of the three numbers.
Solution Implementation
1from collections import Counter
2from typing import List
3
4class Solution:
5 def singleDivisorTriplet(self, nums: List[int]) -> int:
6 # A helper function to check if only one of a, b, or c is a divisor of their sum
7 def is_single_divisor_triplet(a: int, b: int, c: int) -> bool:
8 sum_triplet = a + b + c
9 return sum(sum_triplet % x == 0 for x in [a, b, c]) == 1
10
11 # Count the occurrences of each number in the list
12 num_counter = Counter(nums)
13 count_triplets = 0 # Initialize the count of valid triplets
14
15 # Iterate over all possible combinations of a, b, and c
16 for a, count_a in num_counter.items():
17 for b, count_b in num_counter.items():
18 for c, count_c in num_counter.items():
19 # Check if the current combination is a single divisor triplet
20 if is_single_divisor_triplet(a, b, c):
21 # If any two numbers are equal, adjust the count accordingly
22 if a == b and b == c:
23 # All numbers are the same
24 count_triplets += count_a * (count_a - 1) * (count_a - 2) // 6
25 elif a == b:
26 count_triplets += count_a * (count_a - 1) // 2 * count_c
27 elif a == c:
28 count_triplets += count_a * (count_a - 1) // 2 * count_b
29 elif b == c:
30 count_triplets += count_a * count_b * (count_b - 1) // 2
31 else:
32 # All numbers are different
33 count_triplets += count_a * count_b * count_c
34
35 # Since each triplet is counted 6 times (all permutations), we divide by 6
36 return count_triplets // 6
37
38# The above code can be used as follows:
39# solution = Solution()
40# result = solution.singleDivisorTriplet([nums])
41
1class Solution {
2 public long singleDivisorTriplet(int[] nums) {
3 // Initialize a counter array to count the occurrences of each number up to 100.
4 int[] count = new int[101];
5 for (int num : nums) {
6 count[num]++;
7 }
8
9 // Initialize a variable to store the answer.
10 long answer = 0;
11
12 // Iterate through all possible trios and calculate the sum.
13 for (int i = 1; i <= 100; i++) {
14 for (int j = 1; j <= 100; j++) {
15 for (int k = 1; k <= 100; k++) {
16 // Get the count of each number in the trio.
17 int countI = count[i];
18 int countJ = count[j];
19 int countK = count[k];
20
21 int sum = i + j + k;
22
23 // Check if exactly one number divides the sum.
24 int divisorCount = 0;
25 divisorCount += sum % i == 0 ? 1 : 0;
26 divisorCount += sum % j == 0 ? 1 : 0;
27 divisorCount += sum % k == 0 ? 1 : 0;
28
29 // If not exactly one divisor, then continue to the next trio.
30 if (divisorCount != 1) {
31 continue;
32 }
33
34 // Calculate the number of valid configurations.
35 if (i == j && i == k) {
36 // All three numbers are the same.
37 answer += (long) countI * (countI - 1) * (countI - 2) / 6;
38 } else if (i == j) {
39 // i and j are the same, but k is different.
40 answer += (long) countI * (countI - 1) / 2 * countK;
41 } else if (i == k) {
42 // i and k are the same, but j is different.
43 answer += (long) countI * (countI - 1) / 2 * countJ;
44 } else if (j == k) {
45 // j and k are the same, but i is different.
46 answer += (long) countI * countJ * (countJ - 1) / 2;
47 } else {
48 // All three numbers are different.
49 answer += (long) countI * countJ * countK;
50 }
51 }
52 }
53 }
54
55 // Return the computed answer.
56 return answer;
57 }
58}
59
1#include <vector>
2using namespace std;
3
4class Solution {
5public:
6 long long singleDivisorTriplet(vector<int>& nums) {
7 // Counter array to keep the frequency of each number from 1 to 100
8 vector<int> frequency(101, 0);
9 // Fill the counter with frequencies of numbers in the input vector nums
10 for (int number : nums) {
11 frequency[number]++;
12 }
13
14 long long answer = 0; // Variable to store the final count of triplets
15
16 // Check all possible combinations of three numbers (i, j, k)
17 for (int i = 1; i <= 100; ++i) {
18 for (int j = 1; j <= 100; ++j) {
19 for (int k = 1; k <= 100; ++k) {
20 // Fetch the frequencies of the current combination
21 int freq_i = frequency[i];
22 int freq_j = frequency[j];
23 int freq_k = frequency[k];
24
25 // Calculate the sum of the triplet
26 int sum = i + j + k;
27 // Count the number of divisors the sum has from the triplet
28 int divisorCount = (sum % i == 0) + (sum % j == 0) + (sum % k == 0);
29
30 // We are only interested in triplets where exactly one of the numbers divides the sum
31 if (divisorCount != 1) continue;
32
33 // Now, handle the cases depending on the uniqueness of i, j, k
34 if (i == j && i == k) {
35 // All numbers are the same
36 answer += freq_i * (freq_i - 1) * (freq_i - 2) / 6; // Combination of nC3
37 } else if (i == j) {
38 // i and j are the same, k is different
39 answer += (long long) freq_i * (freq_i - 1) / 2 * freq_k; // Combination of nC2 times the count of k
40 } else if (i == k) {
41 // i and k are the same, j is different
42 answer += (long long) freq_i * (freq_i - 1) / 2 * freq_j; // Combination of nC2 times the count of j
43 } else if (j == k) {
44 // j and k are the same, i is different
45 answer += (long long) freq_j * (freq_j - 1) / 2 * freq_i; // Combination of nC2 times the count of i
46 } else {
47 // i, j, and k are all different
48 answer += (long long) freq_i * freq_j * freq_k; // Just the product of the counts
49 }
50 }
51 }
52 }
53
54 // Return the total count of single divisor triplets
55 return answer;
56 }
57};
58
1const MAX_NUM = 100;
2
3// Function to count single divisor triplets
4function singleDivisorTriplet(nums: number[]): number {
5 // Initialize a counter array to keep the frequency of each number from 1 to 100
6 const frequency: number[] = new Array(MAX_NUM + 1).fill(0);
7
8 // Fill the counter with frequencies of numbers in the input array nums
9 nums.forEach(number => {
10 frequency[number]++;
11 });
12
13 let answer: number = 0; // Variable to store the final count of triplets
14
15 // Check all possible combinations of three numbers (i, j, k)
16 for (let i = 1; i <= MAX_NUM; ++i) {
17 for (let j = 1; j <= MAX_NUM; ++j) {
18 for (let k = 1; k <= MAX_NUM; ++k) {
19 // Fetch the frequencies of the current combination
20 const freqI: number = frequency[i];
21 const freqJ: number = frequency[j];
22 const freqK: number = frequency[k];
23
24 // Calculate the sum of the triplet
25 const sum: number = i + j + k;
26 // Count the number of divisors the sum has from the triplet
27 const divisorCount: number = (sum % i === 0 ? 1 : 0)
28 + (sum % j === 0 ? 1 : 0)
29 + (sum % k === 0 ? 1 : 0);
30
31 // We are only interested in triplets where exactly one of the numbers divides the sum
32 if (divisorCount !== 1) continue;
33
34 // Handle the cases depending on the uniqueness of i, j, k
35 if (i === j && i === k) {
36 // All numbers are the same (choose 3 from freqI)
37 answer += freqI * (freqI - 1) * (freqI - 2) / 6;
38 } else if (i === j) {
39 // i and j are the same, k is different (choose 2 from freqI and multiply by freqK)
40 answer += freqI * (freqI - 1) / 2 * freqK;
41 } else if (i === k) {
42 // i and k are the same, j is different (choose 2 from freqI and multiply by freqJ)
43 answer += freqI * (freqI - 1) / 2 * freqJ;
44 } else if (j === k) {
45 // j and k are the same, i is different (choose 2 from freqJ and multiply by freqI)
46 answer += freqJ * (freqJ - 1) / 2 * freqI;
47 } else {
48 // i, j, and k are all different (multiply counts of freqI, freqJ, freqK)
49 answer += freqI * freqJ * freqK;
50 }
51 }
52 }
53 }
54
55 // Return the total count of single divisor triplets
56 return answer;
57}
58
59// Sample usage
60const sampleNums: number[] = [1, 2, 3];
61const sampleResult: number = singleDivisorTriplet(sampleNums);
62console.log(sampleResult); // Output the result
63
Time and Space Complexity
The given Python code aims to count the number of triplets in a list where exactly one of the three numbers is a divisor of the sum of the triplet.
Time Complexity:
The time complexity of the algorithm can be analyzed based on the nested loops and the operations performed within them.
We iterate over all unique elements in nums
up to three times due to the nested loops. If there are k
unique elements, then the triple-nested loop runs O(k^3)
times. The check
function is called inside the innermost loop, which operates in O(1)
time because it simply performs a fixed series of modulo operations and comparisons.
For each unique triplet (a, b, c)
, we calculate the number of such triplets considering their counts in the original list. The increments to ans
involve multiplication, which is an O(1)
operation.
Therefore, the overall time complexity is O(k^3)
.
Space Complexity:
The space complexity can be determined by the additional space used by the algorithm, which is not part of the input.
-
The
counter
variable is a Counter object, which stores the frequency of each unique number innums
. It occupiesO(k)
space, wherek
is the number of unique numbers innums
. -
Other than that, the space usage is constant
O(1)
because we only use a fixed number of extra variables (ans
,a
,b
,c
,cnt1
,cnt2
,cnt3
, and space for thecheck
function's return value).
Thus, the overall space complexity is O(k)
.
Learn more about how to find time and space complexity quickly using problem constraints.
Which data structure is used in a depth first search?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!