Skip to content

Commit 0a8802f

Browse files
author
Gonzalo Diaz
committed
[Hacker Rank] Interview Preparation Kit: Recursion: Davis' Staircase. Solved ✅.
1 parent c2ed06d commit 0a8802f

File tree

6 files changed

+340
-0
lines changed

6 files changed

+340
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# [Recursion: Davis' Staircase](https://www.hackerrank.com/challenges/ctci-recursive-staircase)
2+
3+
Find the number of ways to get from the bottom of a staircase
4+
to the top if you can jump 1, 2, or 3 stairs at a time.
5+
6+
- Difficulty: `#medium`
7+
- Category: `#ProblemSolvingIntermediate`
8+
9+
## Failed solution
10+
11+
This solution correctly calculates the result. The problem is its performance,
12+
since due to the traversal of the recursion tree,
13+
it is eventually easy to reach repeated cases that are recalculated each time.
14+
15+
```typescript
16+
def step_perms_compute(n: number): number
17+
if (n == 0) {
18+
return 0
19+
}
20+
if (n == 1) {
21+
return 1
22+
}
23+
if (n == 2) {
24+
return 2
25+
}
26+
if (n == 3) {
27+
return 4
28+
}
29+
30+
return
31+
step_perms_compute(n - 3) +
32+
step_perms_compute(n - 2) +
33+
step_perms_compute(n - 1)
34+
```
35+
36+
## Alternative solution
37+
38+
The final solution introduces a simple caching mechanism,
39+
so that repeated cases are not recalculated.
40+
41+
The trade-off is that the algorithm now requires
42+
more memory to run in less time.
43+
44+
## Generalized solution
45+
46+
In order to comply with some clean code best practices,
47+
I noticed that the step limit in the algorithm is a hard-coded number,
48+
so to comply with the "no magic numbers" rule,
49+
I was forced to find a more generalized solution.
50+
51+
Then I found the following pattern:
52+
53+
- First cases are:
54+
55+
$$ \begin{matrix}
56+
\text{stepPerms(0)} = 0 \\
57+
\text{stepPerms(1)} = 1 \\
58+
\text{stepPerms(2)} = 2 \\
59+
\end{matrix}
60+
$$
61+
62+
- Next step combinations above 2 and less than the step limit are:
63+
64+
$$ \text{stepPerms(number of steps)} = 2^\text{number of steps} + 1 $$
65+
66+
- When `number of steps` are above the limit, the pattern is
67+
the sum of latest `number of steps` previous calls of
68+
`stepPerms(x)` results as follows:
69+
70+
$$ \displaystyle\sum_{
71+
i=\text{number of steps} - \text{limit}}
72+
^\text{number of steps}
73+
stepPerms(\text{number of steps} - i)
74+
$$
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# [Recursion: Davis' Staircase](https://www.hackerrank.com/challenges/ctci-recursive-staircase)
2+
3+
Find the number of ways to get from the bottom of a staircase
4+
to the top if you can jump 1, 2, or 3 stairs at a time.
5+
6+
- Difficulty: `#medium`
7+
- Category: `#ProblemSolvingIntermediate`
8+
9+
Davis has a number of staircases in his house and he likes to
10+
climb each staircase `1`, `2`, or `3` steps at a time.
11+
Being a very precocious child, he wonders how many ways there
12+
are to reach the top of the staircase.
13+
14+
Given the respective heights for each of the staircases in his house,
15+
find and print the number of ways he can climb each staircase,
16+
module $10^10 + 7 $ on a new line.
17+
18+
## Example
19+
20+
`n = 5`
21+
22+
The staircase has `5` steps. Davis can step on the following sequences of steps:
23+
24+
```text
25+
1 1 1 1 1
26+
1 1 1 2
27+
1 1 2 1
28+
1 2 1 1
29+
2 1 1 1
30+
1 2 2
31+
2 2 1
32+
2 1 2
33+
1 1 3
34+
1 3 1
35+
3 1 1
36+
2 3
37+
3 2
38+
```
39+
40+
There are `13` possible ways he can take these `5` steps and `13 modulo 10000000007`
41+
42+
## Function Description
43+
44+
Complete the stepPerms function using recursion in the editor below.
45+
46+
stepPerms has the following parameter(s):
47+
48+
- int n: the number of stairs in the staircase
49+
50+
## Returns
51+
52+
int: the number of ways Davis can climb the staircase, modulo 10000000007
53+
54+
## Input Format
55+
56+
The first line contains a single integer, `s`, the number of staircases in his house.
57+
Each of the following `s` lines contains a single integer,
58+
`n`, the height of staircase `i`.
59+
60+
## Constraints
61+
62+
- $ 1 \leq s \leq 5 $
63+
- $ 1 \leq n \leq 36 $
64+
65+
## Subtasks
66+
67+
- 1 \leq n \leq 20 for `50%` of the maximum score.
68+
69+
## Sample Input
70+
71+
```text
72+
STDIN Function
73+
----- --------
74+
3 s = 3 (number of staircases)
75+
1 first staircase n = 1
76+
3 second n = 3
77+
7 third n = 7
78+
```
79+
80+
## Sample Output
81+
82+
```text
83+
1
84+
4
85+
44
86+
```
87+
88+
## Explanation
89+
90+
Let's calculate the number of ways of climbing
91+
the first two of the Davis' `s = 3` staircases:
92+
93+
1. The first staircase only has `n = 1` step,
94+
so there is only one way for him to
95+
climb it (i.e., by jumping `1` step). Thus, we print `1` on a new line.
96+
97+
2. The second staircase has `n = 3` steps and he can climb it in any of the
98+
four following ways:
99+
100+
1. 1 -> 1 -> 1
101+
2. 1 -> 2
102+
3. 2 -> 1
103+
4. 3
104+
105+
Thus, we print `4` on a new line.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* @link Problem definition [[docs/hackerrank/interview_preparation_kit/recursion_and_backtracking/ctci-recursive-staircase.md]]
3+
* @see Solution Notes: [[docs/hackerrank/interview_preparation_kit/recursion_and_backtracking/ctci-recursive-staircase-solution-notes.md]]
4+
*/
5+
6+
const TOP_LIMIT = 10 ** 10 + 7;
7+
const STEPS_LIMIT = 3;
8+
9+
export function stepPermsComputWithCache(nSteps, cache, stepsLimit) {
10+
if (nSteps >= 0 && nSteps <= 2) {
11+
return nSteps;
12+
}
13+
14+
const keys = new Set(Object.values(cache));
15+
let result = 0;
16+
17+
for (let i = 1; i <= Math.min(stepsLimit, nSteps); i++) {
18+
const searchKey = nSteps - i;
19+
if (!keys.has(searchKey)) {
20+
cache[searchKey] = stepPermsComputWithCache(searchKey, cache, stepsLimit);
21+
}
22+
23+
result += cache[searchKey];
24+
}
25+
26+
return result + (nSteps <= stepsLimit ? 1 : 0);
27+
}
28+
29+
export function stepPerms(n) {
30+
const initialCache = {};
31+
return stepPermsComputWithCache(n, initialCache, STEPS_LIMIT) % TOP_LIMIT;
32+
}
33+
34+
export default { stepPerms, stepPermsComputWithCache };
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { describe, expect, it } from '@jest/globals';
2+
import { logger as console } from '../../../logger.js';
3+
4+
import {
5+
stepPerms,
6+
stepPermsComputWithCache
7+
} from './ctci_recursive_staircase.js';
8+
import TEST_CASES from './ctci_recursive_staircase.testcases.json';
9+
import TEST_CASES_GENERALIZED from './ctci_recursive_staircase_generalized.testcases.json';
10+
11+
describe('ctci_recursive_staircase', () => {
12+
it('stepPerms test cases', () => {
13+
expect.assertions(8);
14+
15+
TEST_CASES.forEach((testSet) => {
16+
testSet?.tests.forEach((test) => {
17+
const answer = stepPerms(test.input);
18+
19+
console.debug(`stepPerms(${test.input}) solution found: ${answer}`);
20+
21+
expect(answer).toStrictEqual(test.expected);
22+
});
23+
});
24+
});
25+
26+
it('stepPermsComputWithCache test cases', () => {
27+
expect.assertions(3);
28+
29+
TEST_CASES_GENERALIZED.forEach((testSet) => {
30+
testSet?.tests.forEach((test) => {
31+
const initialCache = {};
32+
const answer = stepPermsComputWithCache(
33+
test.input,
34+
initialCache,
35+
test.limit
36+
);
37+
38+
console.debug(
39+
`stepPermsComputWithCache(${test.input}, ${initialCache}, ${test.limit}) solution found: ${answer}`
40+
);
41+
42+
expect(answer).toStrictEqual(test.expected);
43+
});
44+
});
45+
});
46+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
[
2+
{
3+
"title": "Sample Test case 0",
4+
"tests": [
5+
{
6+
"input": 1,
7+
"expected": 1
8+
},
9+
{
10+
"input": 3,
11+
"expected": 4
12+
},
13+
{
14+
"input": 7,
15+
"expected": 44
16+
}
17+
]
18+
},
19+
{
20+
"title": "Sample Test case 9",
21+
"tests": [
22+
{
23+
"input": 5,
24+
"expected": 13
25+
},
26+
{
27+
"input": 8,
28+
"expected": 81
29+
}
30+
]
31+
},
32+
{
33+
"title": "Sample Test case 10",
34+
"tests": [
35+
{
36+
"input": 15,
37+
"expected": 5768
38+
},
39+
{
40+
"input": 20,
41+
"expected": 121415
42+
},
43+
{
44+
"input": 27,
45+
"expected": 8646064
46+
}
47+
]
48+
}
49+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
[
2+
{
3+
"title": "Own sample 1",
4+
"tests": [
5+
{
6+
"input": 4,
7+
"limit": 3,
8+
"expected": 7
9+
}
10+
]
11+
},
12+
{
13+
"title": "Own sample 2",
14+
"tests": [
15+
{
16+
"input": 5,
17+
"limit": 4,
18+
"expected": 15
19+
}
20+
]
21+
},
22+
{
23+
"title": "Own sample 3",
24+
"tests": [
25+
{
26+
"input": 6,
27+
"limit": 2,
28+
"expected": 13
29+
}
30+
]
31+
}
32+
]

0 commit comments

Comments
 (0)