Skip to content

Commit 7aae745

Browse files
author
Gonzalo Diaz
committed
[Hacker Rank] Interview Preparation Kit: Dictionaries and Hashmaps: Count Triplets. Solved ✅.
1 parent 8b90f90 commit 7aae745

File tree

7 files changed

+398
-0
lines changed

7 files changed

+398
-0
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# [Dictionaries and Hashmaps: Count Triplets](https://www.hackerrank.com/challenges/count-triplets-1)
2+
3+
- Difficulty: `#medium`
4+
- Category: `#ProblemSolvingIntermediate`
5+
6+
## Failed tries
7+
8+
### Introducing optimizations and cache
9+
10+
This solution is based on the idea of traversing each possible case,
11+
similar to brute force, but introducing a cache mechanism
12+
for each case found and cutting the number of times it would have to be traversed.
13+
14+
This has following problems:
15+
16+
1) The problem is that it preserves the complexity of O(n^3) (same as brute force)
17+
18+
2) "Cognitive Complexity" (measured by sonarlint) it is very high.
19+
This due to the number of nesting.
20+
21+
3) Initially, the innermost calculation assumes that the values in the array
22+
"should" be ordered, that is, it expects that the values to the right of the
23+
currently evaluated value would be larger.
24+
With this idea in mind, an initial ordering of values was introduced.
25+
But the problem requires that the values be in positions i < j < k.
26+
That is why initially an ordering of the input was executed,
27+
but such ordering introduces unexpected possibilities. In an unordered list,
28+
it can produce overcounts of favorable cases.
29+
30+
```python
31+
def count_triplets_with_cache_and_cuts(arr: list[int], r: int) -> int:
32+
33+
# arrc = arr[:]
34+
# arrc = sorted(arrc)
35+
size = len(arr)
36+
37+
cache: dict[tuple[int, int, int], bool] = {}
38+
counter = 0
39+
40+
i_resume = True
41+
i = 0
42+
while i < size - 2 and i_resume:
43+
j_resume = True
44+
j = i + 1
45+
while j < size - 1 and j_resume:
46+
if arr[j] > r * arr[i]:
47+
j_resume = False
48+
49+
k_resume = True
50+
k = j + 1
51+
while k < size and k_resume:
52+
if arr[k] > r * arr[j]:
53+
k_resume = False
54+
55+
triplet = (arr[i], arr[j], arr[k])
56+
57+
if triplet in cache and cache[triplet]:
58+
counter += 1
59+
else:
60+
if r * arr[i] == arr[j] and r * arr[j] == arr[k]:
61+
cache[triplet] = True
62+
counter += 1
63+
else:
64+
cache[triplet] = False
65+
66+
k += 1
67+
j += 1
68+
i += 1
69+
70+
return counter
71+
```
72+
73+
### Reducing complexity
74+
75+
This solutions reduce complexity to O(N), but has the same problem
76+
with unordered lists as the previous case.
77+
78+
```python
79+
def count_triplets_doenst_work_with_unsorted_input(arr: list[int], r: int) -> int:
80+
count: int = 0
81+
82+
if len(arr) < 3:
83+
return count
84+
85+
counter = Counter(arr)
86+
size = len(counter)
87+
limit = size - 2
88+
89+
i = 0
90+
for k, v in counter.items():
91+
knext = k * r
92+
knext_next = k * r * r
93+
94+
if i < limit and knext in counter and knext_next in counter:
95+
next_elem_cnt = counter[knext]
96+
next_next_elem_cnt = counter[knext_next]
97+
count += v * (next_elem_cnt * next_next_elem_cnt)
98+
elif r == 1:
99+
count += math.factorial(v) // (math.factorial(3) * math.factorial(v - 3))
100+
101+
i += 1
102+
103+
return count
104+
```
105+
106+
## Working solution
107+
108+
This solution in O(N), is based on considering that each
109+
number analyzed is in the center of the triplet and asks
110+
"how many" possible cases there are on the left and
111+
right to calculate how many possible triplets are formed.
112+
113+
- Source: [Hackerrank - Count Triplets Solution](https://www.thepoorcoder.com/hackerrank-count-triplets-solution/)
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# [Dictionaries and Hashmaps: Count Triplets](https://www.hackerrank.com/challenges/count-triplets-1)
2+
3+
- Difficulty: `#medium`
4+
- Category: `#ProblemSolvingIntermediate`
5+
6+
You are given an array and you need to find number of
7+
tripets of indices (i, j, k) such that the elements at
8+
those indices are in geometric progression for a given
9+
common ratio `r` and $ i < j < k $.
10+
11+
## Example
12+
13+
`arr = [1, 4, 16, 64] r = 4`
14+
15+
There are `[1, 4, 16]` and `[4, 16, 64]` at indices (0, 1, 2) and (1, 2, 3).
16+
Return `2`.
17+
18+
## Function Description
19+
20+
Complete the countTriplets function in the editor below.
21+
22+
countTriplets has the following parameter(s):
23+
24+
- `int arr[n]`: an array of integers
25+
- `int r`: the common ratio
26+
27+
## Returns
28+
29+
- `int`: the number of triplets
30+
31+
## Input Format
32+
33+
The first line contains two space-separated integers `n` and `r`,
34+
the size of `arr` and the common ratio.
35+
The next line contains `n` space-seperated integers `arr[i]`.
36+
37+
## Constraints
38+
39+
- $ 1 \leq n \leq 10^5 $
40+
- $ 1 \leq r \leq 10^9 $
41+
- $ 1 \leq arr[i] \leq 10^9 $
42+
43+
## Sample Input 0
44+
45+
```text
46+
4 2
47+
1 2 2 4
48+
```
49+
50+
## Sample Output 0
51+
52+
```text
53+
2
54+
```
55+
56+
## Explanation 0
57+
58+
There are `2` triplets in satisfying our criteria,
59+
whose indices are (0, 1, 3) and (0, 2, 3)
60+
61+
## Sample Input 1
62+
63+
```text
64+
6 3
65+
1 3 9 9 27 81
66+
```
67+
68+
## Sample Output 1
69+
70+
```text
71+
6
72+
```
73+
74+
## Explanation 1
75+
76+
The triplets satisfying are index
77+
`(0, 1, 2)`, `(0, 1, 3)`, `(1, 2, 4)`, `(1, 3, 4)`, `(2, 4, 5)` and `(3, 4, 5)`.
78+
79+
## Sample Input 2
80+
81+
```text
82+
5 5
83+
1 5 5 25 125
84+
```
85+
86+
## Sample Output 2
87+
88+
```text
89+
4
90+
```
91+
92+
## Explanation 2
93+
94+
The triplets satisfying are index
95+
`(0, 1, 3)`, `(0, 2, 3)`, `(1, 2, 3)`, `(2, 3, 4)`.
96+
97+
## Appendix
98+
99+
[Solution notes](count_triplets_1-solution-notes.md)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* @link Problem definition [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/count_triplets_1.md]]
3+
* @see Solution Notes: [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/count_triplets_1-solution-notes.md]]
4+
*/
5+
import { logger as console } from '../../../logger.js';
6+
7+
export function countTriplets(arr, ratio) {
8+
const size = arr.length;
9+
let counter = 0;
10+
11+
for (let i = 0; i < size - 2; i++) {
12+
for (let j = i + 1; j < size - 1; j++) {
13+
for (let k = j + 1; k < size; k++) {
14+
console.debug(`${arr[i]}, ${arr[j]}, ${arr[k]}`);
15+
16+
if (ratio * arr[i] === arr[j] && ratio * arr[j] === arr[k]) {
17+
counter += 1;
18+
}
19+
}
20+
}
21+
}
22+
23+
return counter;
24+
}
25+
26+
export default { countTriplets };
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { describe, expect, it } from '@jest/globals';
2+
import { logger as console } from '../../../logger.js';
3+
4+
import { countTriplets } from './count_triplets_1_bruteforce.js';
5+
6+
const SMALL_TEST_CASES = [
7+
{
8+
title: 'Sample Test Case 0',
9+
input: [1, 2, 2, 4],
10+
r: 2,
11+
expected: 2
12+
},
13+
{
14+
title: 'Sample Test Case 1',
15+
input: [1, 3, 9, 9, 27, 81],
16+
r: 3,
17+
expected: 6
18+
},
19+
{
20+
title: 'Sample Test Case 1 (unsorted)',
21+
input: [9, 3, 1, 81, 9, 27],
22+
r: 3,
23+
expected: 1
24+
},
25+
{
26+
title: 'Sample Test Case 12',
27+
input: [1, 5, 5, 25, 125],
28+
r: 5,
29+
expected: 4
30+
}
31+
];
32+
33+
describe('count_triplets_1', () => {
34+
it('countTriplets test cases', () => {
35+
expect.assertions(4);
36+
37+
SMALL_TEST_CASES.forEach((test) => {
38+
const answer = countTriplets(test.input, test.r);
39+
40+
console.debug(
41+
`countTriplets(${test.input}, ${test.r}) solution found: ${answer}`
42+
);
43+
44+
expect(answer).toStrictEqual(test.expected);
45+
});
46+
});
47+
});
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { describe, expect, it } from '@jest/globals';
2+
import { logger as console } from '../../../logger.js';
3+
4+
import { countTriplets } from './count_triplets_1_optmized.js';
5+
import SMALL_TEST_CASES from './count_triplets_1_testcases.json';
6+
7+
const BIG_TEST_CASES = [
8+
{
9+
title: 'Sample Test Case 2',
10+
input: [
11+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
12+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
13+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
14+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
15+
],
16+
r: 1,
17+
expected: 161700
18+
}
19+
];
20+
21+
describe('count_triplets_1 (optimized)', () => {
22+
it('countTriplets small test cases', () => {
23+
expect.assertions(4);
24+
25+
SMALL_TEST_CASES.forEach((test) => {
26+
const answer = countTriplets(test.input, test.r);
27+
28+
console.debug(
29+
`countTriplets(${test.input}, ${test.r}) solution found: ${answer}`
30+
);
31+
32+
expect(answer).toStrictEqual(test.expected);
33+
});
34+
});
35+
36+
it('countTriplets big test cases', () => {
37+
expect.assertions(1);
38+
39+
BIG_TEST_CASES.forEach((test) => {
40+
const answer = countTriplets(test.input, test.r);
41+
42+
console.debug(
43+
`countTriplets(${test.input}, ${test.r}) solution found: ${answer}`
44+
);
45+
46+
expect(answer).toStrictEqual(test.expected);
47+
});
48+
});
49+
});
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* @link Problem definition [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/count_triplets_1.md]]
3+
* @see Solution Notes: [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/count_triplets_1-solution-notes.md]]
4+
*/
5+
6+
export function countTriplets(arr, ratio) {
7+
let triplets = 0;
8+
9+
const aCounter = arr.reduce((accumulator, entry) => {
10+
if (entry in accumulator) {
11+
accumulator[entry] += 1;
12+
} else {
13+
accumulator[entry] = 1;
14+
}
15+
return accumulator;
16+
}, {});
17+
18+
const bCounter = {};
19+
20+
arr.forEach((x) => {
21+
const j = Math.floor(x / ratio);
22+
const k = x * ratio;
23+
aCounter[x] -= 1;
24+
if (bCounter[j] && aCounter[k] && x % ratio === 0) {
25+
triplets += bCounter[j] * aCounter[k];
26+
}
27+
28+
if (x in bCounter) {
29+
bCounter[x] += 1;
30+
} else {
31+
bCounter[x] = 1;
32+
}
33+
});
34+
35+
return triplets;
36+
}
37+
38+
export default { countTriplets };

0 commit comments

Comments
 (0)