-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Hacker Rank] Interview Preparation Kit: Dictionaries and Hashmaps: C…
…ount Triplets. Solved ✅.
- Loading branch information
Gonzalo Diaz
committed
Jul 19, 2024
1 parent
8b90f90
commit 7aae745
Showing
7 changed files
with
398 additions
and
0 deletions.
There are no files selected for viewing
113 changes: 113 additions & 0 deletions
113
...ew_preparation_kit/dictionaries_and_hashmaps/count_triplets_1-solution-notes.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
# [Dictionaries and Hashmaps: Count Triplets](https://www.hackerrank.com/challenges/count-triplets-1) | ||
|
||
- Difficulty: `#medium` | ||
- Category: `#ProblemSolvingIntermediate` | ||
|
||
## Failed tries | ||
|
||
### Introducing optimizations and cache | ||
|
||
This solution is based on the idea of traversing each possible case, | ||
similar to brute force, but introducing a cache mechanism | ||
for each case found and cutting the number of times it would have to be traversed. | ||
|
||
This has following problems: | ||
|
||
1) The problem is that it preserves the complexity of O(n^3) (same as brute force) | ||
|
||
2) "Cognitive Complexity" (measured by sonarlint) it is very high. | ||
This due to the number of nesting. | ||
|
||
3) Initially, the innermost calculation assumes that the values in the array | ||
"should" be ordered, that is, it expects that the values to the right of the | ||
currently evaluated value would be larger. | ||
With this idea in mind, an initial ordering of values was introduced. | ||
But the problem requires that the values be in positions i < j < k. | ||
That is why initially an ordering of the input was executed, | ||
but such ordering introduces unexpected possibilities. In an unordered list, | ||
it can produce overcounts of favorable cases. | ||
|
||
```python | ||
def count_triplets_with_cache_and_cuts(arr: list[int], r: int) -> int: | ||
|
||
# arrc = arr[:] | ||
# arrc = sorted(arrc) | ||
size = len(arr) | ||
|
||
cache: dict[tuple[int, int, int], bool] = {} | ||
counter = 0 | ||
|
||
i_resume = True | ||
i = 0 | ||
while i < size - 2 and i_resume: | ||
j_resume = True | ||
j = i + 1 | ||
while j < size - 1 and j_resume: | ||
if arr[j] > r * arr[i]: | ||
j_resume = False | ||
|
||
k_resume = True | ||
k = j + 1 | ||
while k < size and k_resume: | ||
if arr[k] > r * arr[j]: | ||
k_resume = False | ||
|
||
triplet = (arr[i], arr[j], arr[k]) | ||
|
||
if triplet in cache and cache[triplet]: | ||
counter += 1 | ||
else: | ||
if r * arr[i] == arr[j] and r * arr[j] == arr[k]: | ||
cache[triplet] = True | ||
counter += 1 | ||
else: | ||
cache[triplet] = False | ||
|
||
k += 1 | ||
j += 1 | ||
i += 1 | ||
|
||
return counter | ||
``` | ||
|
||
### Reducing complexity | ||
|
||
This solutions reduce complexity to O(N), but has the same problem | ||
with unordered lists as the previous case. | ||
|
||
```python | ||
def count_triplets_doenst_work_with_unsorted_input(arr: list[int], r: int) -> int: | ||
count: int = 0 | ||
|
||
if len(arr) < 3: | ||
return count | ||
|
||
counter = Counter(arr) | ||
size = len(counter) | ||
limit = size - 2 | ||
|
||
i = 0 | ||
for k, v in counter.items(): | ||
knext = k * r | ||
knext_next = k * r * r | ||
|
||
if i < limit and knext in counter and knext_next in counter: | ||
next_elem_cnt = counter[knext] | ||
next_next_elem_cnt = counter[knext_next] | ||
count += v * (next_elem_cnt * next_next_elem_cnt) | ||
elif r == 1: | ||
count += math.factorial(v) // (math.factorial(3) * math.factorial(v - 3)) | ||
|
||
i += 1 | ||
|
||
return count | ||
``` | ||
|
||
## Working solution | ||
|
||
This solution in O(N), is based on considering that each | ||
number analyzed is in the center of the triplet and asks | ||
"how many" possible cases there are on the left and | ||
right to calculate how many possible triplets are formed. | ||
|
||
- Source: [Hackerrank - Count Triplets Solution](https://www.thepoorcoder.com/hackerrank-count-triplets-solution/) |
99 changes: 99 additions & 0 deletions
99
...kerrank/interview_preparation_kit/dictionaries_and_hashmaps/count_triplets_1.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
# [Dictionaries and Hashmaps: Count Triplets](https://www.hackerrank.com/challenges/count-triplets-1) | ||
|
||
- Difficulty: `#medium` | ||
- Category: `#ProblemSolvingIntermediate` | ||
|
||
You are given an array and you need to find number of | ||
tripets of indices (i, j, k) such that the elements at | ||
those indices are in geometric progression for a given | ||
common ratio `r` and $ i < j < k $. | ||
|
||
## Example | ||
|
||
`arr = [1, 4, 16, 64] r = 4` | ||
|
||
There are `[1, 4, 16]` and `[4, 16, 64]` at indices (0, 1, 2) and (1, 2, 3). | ||
Return `2`. | ||
|
||
## Function Description | ||
|
||
Complete the countTriplets function in the editor below. | ||
|
||
countTriplets has the following parameter(s): | ||
|
||
- `int arr[n]`: an array of integers | ||
- `int r`: the common ratio | ||
|
||
## Returns | ||
|
||
- `int`: the number of triplets | ||
|
||
## Input Format | ||
|
||
The first line contains two space-separated integers `n` and `r`, | ||
the size of `arr` and the common ratio. | ||
The next line contains `n` space-seperated integers `arr[i]`. | ||
|
||
## Constraints | ||
|
||
- $ 1 \leq n \leq 10^5 $ | ||
- $ 1 \leq r \leq 10^9 $ | ||
- $ 1 \leq arr[i] \leq 10^9 $ | ||
|
||
## Sample Input 0 | ||
|
||
```text | ||
4 2 | ||
1 2 2 4 | ||
``` | ||
|
||
## Sample Output 0 | ||
|
||
```text | ||
2 | ||
``` | ||
|
||
## Explanation 0 | ||
|
||
There are `2` triplets in satisfying our criteria, | ||
whose indices are (0, 1, 3) and (0, 2, 3) | ||
|
||
## Sample Input 1 | ||
|
||
```text | ||
6 3 | ||
1 3 9 9 27 81 | ||
``` | ||
|
||
## Sample Output 1 | ||
|
||
```text | ||
6 | ||
``` | ||
|
||
## Explanation 1 | ||
|
||
The triplets satisfying are index | ||
`(0, 1, 2)`, `(0, 1, 3)`, `(1, 2, 4)`, `(1, 3, 4)`, `(2, 4, 5)` and `(3, 4, 5)`. | ||
|
||
## Sample Input 2 | ||
|
||
```text | ||
5 5 | ||
1 5 5 25 125 | ||
``` | ||
|
||
## Sample Output 2 | ||
|
||
```text | ||
4 | ||
``` | ||
|
||
## Explanation 2 | ||
|
||
The triplets satisfying are index | ||
`(0, 1, 3)`, `(0, 2, 3)`, `(1, 2, 3)`, `(2, 3, 4)`. | ||
|
||
## Appendix | ||
|
||
[Solution notes](count_triplets_1-solution-notes.md) |
26 changes: 26 additions & 0 deletions
26
...errank/interview_preparation_kit/dictionaries_and_hashmaps/count_triplets_1_bruteforce.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/** | ||
* @link Problem definition [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/count_triplets_1.md]] | ||
* @see Solution Notes: [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/count_triplets_1-solution-notes.md]] | ||
*/ | ||
import { logger as console } from '../../../logger.js'; | ||
|
||
export function countTriplets(arr, ratio) { | ||
const size = arr.length; | ||
let counter = 0; | ||
|
||
for (let i = 0; i < size - 2; i++) { | ||
for (let j = i + 1; j < size - 1; j++) { | ||
for (let k = j + 1; k < size; k++) { | ||
console.debug(`${arr[i]}, ${arr[j]}, ${arr[k]}`); | ||
|
||
if (ratio * arr[i] === arr[j] && ratio * arr[j] === arr[k]) { | ||
counter += 1; | ||
} | ||
} | ||
} | ||
} | ||
|
||
return counter; | ||
} | ||
|
||
export default { countTriplets }; |
47 changes: 47 additions & 0 deletions
47
...k/interview_preparation_kit/dictionaries_and_hashmaps/count_triplets_1_bruteforce.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { describe, expect, it } from '@jest/globals'; | ||
import { logger as console } from '../../../logger.js'; | ||
|
||
import { countTriplets } from './count_triplets_1_bruteforce.js'; | ||
|
||
const SMALL_TEST_CASES = [ | ||
{ | ||
title: 'Sample Test Case 0', | ||
input: [1, 2, 2, 4], | ||
r: 2, | ||
expected: 2 | ||
}, | ||
{ | ||
title: 'Sample Test Case 1', | ||
input: [1, 3, 9, 9, 27, 81], | ||
r: 3, | ||
expected: 6 | ||
}, | ||
{ | ||
title: 'Sample Test Case 1 (unsorted)', | ||
input: [9, 3, 1, 81, 9, 27], | ||
r: 3, | ||
expected: 1 | ||
}, | ||
{ | ||
title: 'Sample Test Case 12', | ||
input: [1, 5, 5, 25, 125], | ||
r: 5, | ||
expected: 4 | ||
} | ||
]; | ||
|
||
describe('count_triplets_1', () => { | ||
it('countTriplets test cases', () => { | ||
expect.assertions(4); | ||
|
||
SMALL_TEST_CASES.forEach((test) => { | ||
const answer = countTriplets(test.input, test.r); | ||
|
||
console.debug( | ||
`countTriplets(${test.input}, ${test.r}) solution found: ${answer}` | ||
); | ||
|
||
expect(answer).toStrictEqual(test.expected); | ||
}); | ||
}); | ||
}); |
49 changes: 49 additions & 0 deletions
49
...nk/interview_preparation_kit/dictionaries_and_hashmaps/count_triplets_1_optimized.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { describe, expect, it } from '@jest/globals'; | ||
import { logger as console } from '../../../logger.js'; | ||
|
||
import { countTriplets } from './count_triplets_1_optmized.js'; | ||
import SMALL_TEST_CASES from './count_triplets_1_testcases.json'; | ||
|
||
const BIG_TEST_CASES = [ | ||
{ | ||
title: 'Sample Test Case 2', | ||
input: [ | ||
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, | ||
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, | ||
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, | ||
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 | ||
], | ||
r: 1, | ||
expected: 161700 | ||
} | ||
]; | ||
|
||
describe('count_triplets_1 (optimized)', () => { | ||
it('countTriplets small test cases', () => { | ||
expect.assertions(4); | ||
|
||
SMALL_TEST_CASES.forEach((test) => { | ||
const answer = countTriplets(test.input, test.r); | ||
|
||
console.debug( | ||
`countTriplets(${test.input}, ${test.r}) solution found: ${answer}` | ||
); | ||
|
||
expect(answer).toStrictEqual(test.expected); | ||
}); | ||
}); | ||
|
||
it('countTriplets big test cases', () => { | ||
expect.assertions(1); | ||
|
||
BIG_TEST_CASES.forEach((test) => { | ||
const answer = countTriplets(test.input, test.r); | ||
|
||
console.debug( | ||
`countTriplets(${test.input}, ${test.r}) solution found: ${answer}` | ||
); | ||
|
||
expect(answer).toStrictEqual(test.expected); | ||
}); | ||
}); | ||
}); |
38 changes: 38 additions & 0 deletions
38
...ckerrank/interview_preparation_kit/dictionaries_and_hashmaps/count_triplets_1_optmized.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/** | ||
* @link Problem definition [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/count_triplets_1.md]] | ||
* @see Solution Notes: [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/count_triplets_1-solution-notes.md]] | ||
*/ | ||
|
||
export function countTriplets(arr, ratio) { | ||
let triplets = 0; | ||
|
||
const aCounter = arr.reduce((accumulator, entry) => { | ||
if (entry in accumulator) { | ||
accumulator[entry] += 1; | ||
} else { | ||
accumulator[entry] = 1; | ||
} | ||
return accumulator; | ||
}, {}); | ||
|
||
const bCounter = {}; | ||
|
||
arr.forEach((x) => { | ||
const j = Math.floor(x / ratio); | ||
const k = x * ratio; | ||
aCounter[x] -= 1; | ||
if (bCounter[j] && aCounter[k] && x % ratio === 0) { | ||
triplets += bCounter[j] * aCounter[k]; | ||
} | ||
|
||
if (x in bCounter) { | ||
bCounter[x] += 1; | ||
} else { | ||
bCounter[x] = 1; | ||
} | ||
}); | ||
|
||
return triplets; | ||
} | ||
|
||
export default { countTriplets }; |
Oops, something went wrong.