The chess knight has a unique movement, it may move two squares vertically and one square horizontally, or two squares horizontally and one square vertically (with both forming the shape of an L). The possible movements of chess knight are shown in this diagram:
A chess knight can move as indicated in the chess diagram below:
We have a chess knight and a phone pad as shown below, the knight can only stand on a numeric cell (i.e. blue cell).
Given an integer n, return how many distinct phone numbers of length n we can dial.
You are allowed to place the knight on any numeric cell initially and then you should perform n - 1 jumps to dial a number of length n. All jumps should be valid knight jumps.
As the answer may be very large, return the answer modulo 109 + 7.
Example 1:
Input: n = 1 Output: 10 Explanation: We need to dial a number of length 1, so placing the knight over any numeric cell of the 10 cells is sufficient.
Example 2:
Input: n = 2 Output: 20 Explanation: All the valid number we can dial are [04, 06, 16, 18, 27, 29, 34, 38, 40, 43, 49, 60, 61, 67, 72, 76, 81, 83, 92, 94]
Example 3:
Input: n = 3131 Output: 136006598 Explanation: Please take care of the mod.
Constraints:
1 <= n <= 5000When 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:
The Knight Dialer problem asks us to find how many phone numbers of a certain length can be dialed by a knight on a phone keypad. A brute force approach would try every possible sequence of knight moves to create these phone numbers. It essentially explores all paths the knight can take.
Here's how the algorithm would work step-by-step:
def knight_dialer_brute_force(phone_number_length):
keypad = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
['*', 0, '#']
]
knight_moves = {
0: [4, 6],
1: [6, 8],
2: [7, 9],
3: [4, 8],
4: [0, 3, 9],
5: [],
6: [0, 1, 7],
7: [2, 6],
8: [1, 3],
9: [2, 4]
}
valid_phone_numbers_count = 0
def is_valid_move(row, column):
return 0 <= row < 4 and 0 <= column < 3 and keypad[row][column] != '*' and keypad[row][column] != '#'
def dial_number(current_number, remaining_length):
nonlocal valid_phone_numbers_count
if remaining_length == 1:
valid_phone_numbers_count += 1
return
# Get valid knight moves from current digit.
possible_moves = knight_moves[current_number]
for next_number in possible_moves:
dial_number(next_number, remaining_length - 1)
# Iterate to start number on each digit.
for start_row in range(4):
for start_column in range(3):
start_digit = keypad[start_row][start_column]
if start_digit != '*' and start_digit != '#':
dial_number(start_digit, phone_number_length)
# Because 5 is not considered valid.
return valid_phone_numbers_countThe most efficient way to solve this problem is to realize that the number of ways to dial a number of a specific length only depends on how many ways you could dial numbers of the previous length. We can leverage this fact to avoid recalculating the same information repeatedly. This is done through a technique called dynamic programming.
Here's how the algorithm would work step-by-step:
def knight_dialer(dial_length):
modulo = 10**9 + 7
knight_moves = {
1: [6, 8],
2: [7, 9],
3: [4, 8],
4: [0, 3, 9],
5: [],
6: [0, 1, 7],
7: [2, 6],
8: [1, 3],
9: [2, 4],
0: [4, 6]
}
# Initialize ways to dial a 1-digit number from each starting number.
number_of_ways = [1] * 10
# Iterate to calculate ways for longer numbers.
for _ in range(1, dial_length):
next_number_of_ways = [0] * 10
for start_digit in range(10):
# For each digit, find possible knight moves.
for next_digit in knight_moves[start_digit]:
# Accumulate ways from the previous length.
next_number_of_ways[next_digit] = (next_number_of_ways[next_digit] + number_of_ways[start_digit]) % modulo
number_of_ways = next_number_of_ways
# Sum up the ways from each starting digit for the final result.
total_ways = 0
for ways in number_of_ways:
total_ways = (total_ways + ways) % modulo
return total_ways| Case | How to Handle |
|---|---|
| Input n is zero | Return 0, as there are no possible sequences of length zero. |
| Input n is one | Return 10, as any of the 10 digits can be the starting digit. |
| Large n leading to integer overflow | Use modular arithmetic to prevent the count from exceeding the maximum integer value, and return modulo 10^9 + 7. |
| Negative input for n | Return 0 as a negative sequence length is nonsensical. |
| n close to the maximum integer value | Ensure the DP array or recursion doesn't exceed stack or memory limits, and optimize for space efficiency. |
| All starting numbers lead to zero valid sequences after a few hops | The algorithm will naturally return 0 after exploring all possible paths. |
| Symmetric phone keypad structure and its impact on optimization | Recognize that some starting digits will have the same counts, and optimize DP by only computing for unique starting positions. |
| Cycles in keypad traversal affect correctness | The DP table prevents infinite recursion by storing previously computed results, avoiding cycle issues automatically. |