π Problem Details
- Title:
935. Knight Dialer - Link: https://leetcode.com/problems/knight-dialer/
- Difficulty: Medium
- Tags/Categories: Dynamic-Programming
chess knight movement rules: move two squares vertically and one square horizontally move two squares horizontally and one square vertically input integer
nreturn how many distinct phone numbers of lengthnwe can dial you are allowed to place the knight on any numeric cell all jumps to form a number must be a valid knight move
πIntuition
We model the problem as a graph traversal using the knightβs movement pattern on a phone keypad.
Phone layout:
1 2 3
4 5 6
7 8 9
0
From each digit, we can define which digits are reachable using a knight move.
For example:
- From 1, knight can move to 6 and 8
- From 0, knight can move to 4 and 6
Using this, we construct a map of possible jumps.
Then, we use dynamic programming to count how many numbers of length `n` can be formed from each starting digit.
We initialize our dp array with dp[0][i] = 1 for all digits i (base case: sequences of length 1).
We iterate from length 1 to `n - 1`, updating the number of ways to reach each digit using the knightβs move map.
At the end, sum up all dp[n-1][i] for i in 0..9 to get the final result.
π‘ Explanation of Solution
observations:
1. subproblem?
At each digit and at each step, the knight has a limited number of places it can jump to.
So a subproblem is: how many sequences of length `k` can be formed starting from digit `d`.
2. top-down vs bottom-up
Either approach works. Top-down requires memoization.
Bottom-up can be optimized using rolling arrays of size 10.
3. state definition
Let dp[k][digit] = number of sequences of length k ending at digit `digit`.
4. recurrence relation
dp[k][digit] = sum of all dp[k - 1][prev_digit] where prev_digit can jump to digit
Weβll use the knight move map to compute which digits can jump to which.
β Complexity Analysis
Time Complexity: O(n * 10) = O(n)
- For each of the n steps, we iterate over all 10 digits and compute transitions.
Space Complexity: O(10)
- We only need to store dp values for the current and previous step, so space can be optimized to O(10).
π» Implementation of Solution
class Solution {
public:
int knightDialer(int n) {
const int MOD = 1e9 + 7;
// Define all possible knight moves from each digit on the keypad
vector<vector<int>> moves = {
{4, 6}, // 0 -> can jump to 4 or 6
{6, 8}, // 1 -> can jump to 6 or 8
{7, 9}, // 2 -> can jump to 7 or 9
{4, 8}, // 3 -> can jump to 4 or 8
{0, 3, 9}, // 4 -> can jump to 0, 3, or 9
{}, // 5 -> cannot jump anywhere
{0, 1, 7}, // 6 -> can jump to 0, 1, or 7
{2, 6}, // 7 -> can jump to 2 or 6
{1, 3}, // 8 -> can jump to 1 or 3
{2, 4} // 9 -> can jump to 2 or 4
};
// Initialize the base case: for length = 1, we can dial each digit once
vector<int> prev(10, 1);
// Loop for the remaining n-1 digits
for(int i=1; i < n; i++) {
vector<int> curr(10,0); // To store new counts for this round
// For each digit, calculate number of ways to reach it
for(int digit = 0; digit <= 9; digit++) {
for(int next : moves[digit]) {
// Add the number of ways to reach `next` in the previous step
curr[digit] = (curr[digit] + prev[next]) % MOD;
}
}
// Update for the next iteration
prev = curr;
}
// Sum all valid ways to end at each digit after n moves
int result = 0;
for(int count : prev) {
result = (result + count) % MOD;
}
return result;
}
};