Facebook Pixel

935. Knight Dialer

Problem Description

You have a chess knight and a phone pad. The phone pad is arranged like this:

1 2 3
4 5 6
7 8 9
  0

A chess knight moves in an "L" shape - either 2 squares in one direction and 1 square perpendicular to that, or 1 square in one direction and 2 squares perpendicular to that.

The knight can only stand on numeric cells (0-9), not on the * and # keys.

Given an integer n, you need to find how many distinct phone numbers of length n can be dialed by the knight.

Rules:

  • The knight can start on any numeric cell (0-9)
  • To dial a number of length n, the knight makes exactly n-1 jumps
  • Each jump must be a valid knight move
  • All positions must be on numeric cells

For example, if the knight starts at 1, it can jump to either 6 or 8. If it starts at 5, it cannot make any valid moves since all knight moves from 5 would land outside the numeric cells.

The possible knight moves from each digit are:

  • From 0: can move to 4, 6
  • From 1: can move to 6, 8
  • From 2: can move to 7, 9
  • From 3: can move to 4, 8
  • From 4: can move to 0, 3, 9
  • From 5: no valid moves
  • From 6: can move to 0, 1, 7
  • From 7: can move to 2, 6
  • From 8: can move to 1, 3
  • From 9: can move to 2, 4

Return the total count of distinct phone numbers modulo 10^9 + 7 since the answer can be very large.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that this is a dynamic programming problem where we need to count paths of length n on the phone pad following knight movement rules.

Think about it this way: if we want to dial a number of length n, we need to make n-1 jumps starting from any digit. The total count of numbers we can dial depends on where we can reach after each jump.

Let's start with a simple observation - after placing the knight on any digit initially, we have 10 possible starting positions (digits 0-9). For a number of length 1, we simply have 10 possibilities (one for each starting digit).

For length 2, we need to consider: from each starting digit, where can the knight jump to? The count of 2-digit numbers ending at a particular digit equals the sum of counts from all digits that can reach it in one knight move.

This naturally leads to a recurrence relation. If we know how many ways we can reach each digit after i-1 jumps, we can calculate how many ways to reach each digit after i jumps.

For example, digit 0 can only be reached from digits 4 and 6 (based on knight moves). So if we know there are f[4] ways to reach digit 4 and f[6] ways to reach digit 6 after some jumps, then there are f[4] + f[6] ways to reach digit 0 after one more jump.

We can build this up iteratively: starting with the base case where each digit has count 1 (representing starting positions), we calculate the counts for the next step by summing up the counts from all positions that can reach each digit. We repeat this process n-1 times to get all possible n-digit numbers.

The beauty of this approach is that we don't need to track the actual numbers being dialed - we only care about the count of ways to reach each digit after a certain number of jumps.

Learn more about Dynamic Programming patterns.

Solution Approach

We implement the solution using dynamic programming with space optimization. The approach uses two arrays to track the count of ways to reach each digit.

Data Structure:

  • f: An array of size 10 representing the count of ways to reach each digit (0-9) after the current number of jumps
  • g: A temporary array to calculate the next state

Algorithm Steps:

  1. Initialize the base case: Start with f = [1] * 10, meaning we can start at any of the 10 digits with count 1.

  2. Iterate n-1 times to build numbers of length n:

    • Create a new array g = [0] * 10 to store the next state
    • For each digit, calculate how many ways we can reach it based on the previous state:
      g[0] = f[4] + f[6]        # Can reach 0 from 4 or 6
      g[1] = f[6] + f[8]        # Can reach 1 from 6 or 8
      g[2] = f[7] + f[9]        # Can reach 2 from 7 or 9
      g[3] = f[4] + f[8]        # Can reach 3 from 4 or 8
      g[4] = f[0] + f[3] + f[9] # Can reach 4 from 0, 3, or 9
      g[5] = 0                  # Cannot reach 5 (no incoming moves)
      g[6] = f[0] + f[1] + f[7] # Can reach 6 from 0, 1, or 7
      g[7] = f[2] + f[6]        # Can reach 7 from 2 or 6
      g[8] = f[1] + f[3]        # Can reach 8 from 1 or 3
      g[9] = f[2] + f[4]        # Can reach 9 from 2 or 4
    • Update f = g for the next iteration
  3. Calculate the final result: Sum all values in f and take modulo 10^9 + 7

Why this works:

  • After 0 jumps (length 1 numbers), we have 1 way to be at each digit
  • After 1 jump (length 2 numbers), g[i] tells us how many 2-digit numbers end at digit i
  • After n-1 jumps (length n numbers), f[i] tells us how many n-digit numbers end at digit i
  • The total count is the sum of all possible ending positions

Time Complexity: O(n) - we iterate n-1 times, and each iteration does constant work Space Complexity: O(1) - we only use two arrays of fixed size 10

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through finding all possible 3-digit numbers (n = 3) that can be dialed by the knight.

Initial Setup (n = 1):

  • Start with f = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
  • This represents that we can start at any digit (0-9) with count 1

First Jump (building 2-digit numbers): We calculate where the knight can land after one jump from each starting position:

g[0] = f[4] + f[6] = 1 + 1 = 2         # Knight at 40 or 60
g[1] = f[6] + f[8] = 1 + 1 = 2         # Knight at 61 or 81
g[2] = f[7] + f[9] = 1 + 1 = 2         # Knight at 72 or 92
g[3] = f[4] + f[8] = 1 + 1 = 2         # Knight at 43 or 83
g[4] = f[0] + f[3] + f[9] = 1 + 1 + 1 = 3  # Knight at 04, 34, or 94
g[5] = 0                                # No way to reach 5
g[6] = f[0] + f[1] + f[7] = 1 + 1 + 1 = 3  # Knight at 06, 16, or 76
g[7] = f[2] + f[6] = 1 + 1 = 2         # Knight at 27 or 67
g[8] = f[1] + f[3] = 1 + 1 = 2         # Knight at 18 or 38
g[9] = f[2] + f[4] = 1 + 1 = 2         # Knight at 29 or 49

After first jump: f = [2, 2, 2, 2, 3, 0, 3, 2, 2, 2]

This means:

  • 2 two-digit numbers end at 0 (40 and 60)
  • 2 two-digit numbers end at 1 (61 and 81)
  • 3 two-digit numbers end at 4 (04, 34, and 94)
  • And so on...

Second Jump (building 3-digit numbers): Now we calculate the final positions after the second jump:

g[0] = f[4] + f[6] = 3 + 3 = 6         # 6 ways to reach 0
g[1] = f[6] + f[8] = 3 + 2 = 5         # 5 ways to reach 1
g[2] = f[7] + f[9] = 2 + 2 = 4         # 4 ways to reach 2
g[3] = f[4] + f[8] = 3 + 2 = 5         # 5 ways to reach 3
g[4] = f[0] + f[3] + f[9] = 2 + 2 + 2 = 6  # 6 ways to reach 4
g[5] = 0                                # Still no way to reach 5
g[6] = f[0] + f[1] + f[7] = 2 + 2 + 2 = 6  # 6 ways to reach 6
g[7] = f[2] + f[6] = 2 + 3 = 5         # 5 ways to reach 7
g[8] = f[1] + f[3] = 2 + 2 = 4         # 4 ways to reach 8
g[9] = f[2] + f[4] = 2 + 3 = 5         # 5 ways to reach 9

After second jump: f = [6, 5, 4, 5, 6, 0, 6, 5, 4, 5]

Final Result: Total count = 6 + 5 + 4 + 5 + 6 + 0 + 6 + 5 + 4 + 5 = 46

This means there are 46 distinct 3-digit phone numbers that can be dialed by the knight.

Some example paths:

  • 1→6→0 (forms number 160)
  • 3→4→0 (forms number 340)
  • 7→2→9 (forms number 729)
  • 0→4→3 (forms number 043)

The algorithm efficiently counts all such valid paths without actually generating the numbers themselves.

Solution Implementation

1class Solution:
2    def knightDialer(self, n: int) -> int:
3        # Define the modulo constant for the result
4        MOD = 10**9 + 7
5      
6        # Initialize count array: count[i] represents number of ways to reach digit i
7        # Initially, we can start from any digit (1 way each)
8        current_count = [1] * 10
9      
10        # For each additional move (n-1 moves total)
11        for _ in range(n - 1):
12            # Create new count array for the next iteration
13            next_count = [0] * 10
14          
15            # Calculate possible ways to reach each digit based on knight moves
16            # Knight can reach 0 from positions 4 and 6
17            next_count[0] = current_count[4] + current_count[6]
18          
19            # Knight can reach 1 from positions 6 and 8
20            next_count[1] = current_count[6] + current_count[8]
21          
22            # Knight can reach 2 from positions 7 and 9
23            next_count[2] = current_count[7] + current_count[9]
24          
25            # Knight can reach 3 from positions 4 and 8
26            next_count[3] = current_count[4] + current_count[8]
27          
28            # Knight can reach 4 from positions 0, 3, and 9
29            next_count[4] = current_count[0] + current_count[3] + current_count[9]
30          
31            # Position 5 is unreachable (no moves to/from 5)
32            # next_count[5] remains 0
33          
34            # Knight can reach 6 from positions 0, 1, and 7
35            next_count[6] = current_count[0] + current_count[1] + current_count[7]
36          
37            # Knight can reach 7 from positions 2 and 6
38            next_count[7] = current_count[2] + current_count[6]
39          
40            # Knight can reach 8 from positions 1 and 3
41            next_count[8] = current_count[1] + current_count[3]
42          
43            # Knight can reach 9 from positions 2 and 4
44            next_count[9] = current_count[2] + current_count[4]
45          
46            # Update current count for next iteration
47            current_count = next_count
48      
49        # Return the total number of distinct phone numbers modulo MOD
50        return sum(current_count) % MOD
51
1class Solution {
2    public int knightDialer(int n) {
3        // Define modulo constant for preventing integer overflow
4        final int MOD = (int) 1e9 + 7;
5      
6        // Initialize dp array where dp[i] represents the number of ways 
7        // to reach digit i in the current step
8        long[] dp = new long[10];
9        Arrays.fill(dp, 1); // Initially, knight can start at any digit (1 way each)
10      
11        // Process for n-1 additional hops (we already counted starting position)
12        while (--n > 0) {
13            // Create new dp array for the next step
14            long[] nextDp = new long[10];
15          
16            // Calculate possible ways to reach each digit based on knight's movement rules
17            // Knight moves in L-shape on phone pad:
18            // 1 2 3
19            // 4 5 6  
20            // 7 8 9
21            //   0
22          
23            // Digit 0 can be reached from digits 4 and 6
24            nextDp[0] = (dp[4] + dp[6]) % MOD;
25          
26            // Digit 1 can be reached from digits 6 and 8
27            nextDp[1] = (dp[6] + dp[8]) % MOD;
28          
29            // Digit 2 can be reached from digits 7 and 9
30            nextDp[2] = (dp[7] + dp[9]) % MOD;
31          
32            // Digit 3 can be reached from digits 4 and 8
33            nextDp[3] = (dp[4] + dp[8]) % MOD;
34          
35            // Digit 4 can be reached from digits 0, 3, and 9
36            nextDp[4] = (dp[0] + dp[3] + dp[9]) % MOD;
37          
38            // Digit 5 has no valid knight moves (isolated position)
39            // nextDp[5] remains 0
40          
41            // Digit 6 can be reached from digits 0, 1, and 7
42            nextDp[6] = (dp[0] + dp[1] + dp[7]) % MOD;
43          
44            // Digit 7 can be reached from digits 2 and 6
45            nextDp[7] = (dp[2] + dp[6]) % MOD;
46          
47            // Digit 8 can be reached from digits 1 and 3
48            nextDp[8] = (dp[1] + dp[3]) % MOD;
49          
50            // Digit 9 can be reached from digits 2 and 4
51            nextDp[9] = (dp[2] + dp[4]) % MOD;
52          
53            // Update dp array for next iteration
54            dp = nextDp;
55        }
56      
57        // Sum all possible ending positions and return result with modulo
58        return (int) (Arrays.stream(dp).sum() % MOD);
59    }
60}
61
1class Solution {
2public:
3    int knightDialer(int n) {
4        const int MOD = 1e9 + 7;
5      
6        // dp[i] represents the number of distinct phone numbers ending at digit i
7        // Initially, we can start from any digit (1 way to reach each digit with 1 hop)
8        vector<long long> dp(10, 1);
9      
10        // Process remaining n-1 hops
11        while (--n) {
12            // nextDp[i] will store the number of ways to reach digit i in the next step
13            vector<long long> nextDp(10);
14          
15            // Calculate transitions based on knight's movement rules on the phone pad
16            // Phone pad layout:
17            // 1 2 3
18            // 4 5 6
19            // 7 8 9
20            //   0
21          
22            // Knight can reach 0 from digits 4 and 6
23            nextDp[0] = (dp[4] + dp[6]) % MOD;
24          
25            // Knight can reach 1 from digits 6 and 8
26            nextDp[1] = (dp[6] + dp[8]) % MOD;
27          
28            // Knight can reach 2 from digits 7 and 9
29            nextDp[2] = (dp[7] + dp[9]) % MOD;
30          
31            // Knight can reach 3 from digits 4 and 8
32            nextDp[3] = (dp[4] + dp[8]) % MOD;
33          
34            // Knight can reach 4 from digits 0, 3, and 9
35            nextDp[4] = (dp[0] + dp[3] + dp[9]) % MOD;
36          
37            // Digit 5 has no valid knight moves (isolated position)
38            // nextDp[5] remains 0
39          
40            // Knight can reach 6 from digits 0, 1, and 7
41            nextDp[6] = (dp[0] + dp[1] + dp[7]) % MOD;
42          
43            // Knight can reach 7 from digits 2 and 6
44            nextDp[7] = (dp[2] + dp[6]) % MOD;
45          
46            // Knight can reach 8 from digits 1 and 3
47            nextDp[8] = (dp[1] + dp[3]) % MOD;
48          
49            // Knight can reach 9 from digits 2 and 4
50            nextDp[9] = (dp[2] + dp[4]) % MOD;
51          
52            // Update dp array for the next iteration
53            dp = nextDp;
54        }
55      
56        // Sum up all possible phone numbers of length n ending at each digit
57        return accumulate(dp.begin(), dp.end(), 0LL) % MOD;
58    }
59};
60
1/**
2 * Calculates the number of distinct phone numbers of length n that can be dialed
3 * on a knight's dialer (chess knight movement on a phone keypad).
4 * 
5 * Phone keypad layout:
6 * 1 2 3
7 * 4 5 6
8 * 7 8 9
9 *   0
10 * 
11 * Knight can move in an L-shape from each digit to specific other digits.
12 * 
13 * @param n - The length of the phone number to dial
14 * @returns The total number of distinct phone numbers modulo 10^9 + 7
15 */
16function knightDialer(n: number): number {
17    const MOD: number = 1e9 + 7;
18  
19    // Initialize dynamic programming array
20    // dp[i] represents the number of ways to reach digit i
21    let dp: number[] = Array(10).fill(1);
22  
23    // Process each step from 2 to n
24    while (--n) {
25        // Create new array for current step calculations
26        const nextDp: number[] = Array(10).fill(0);
27      
28        // Calculate possible ways to reach each digit based on knight moves
29        // From digits 4 and 6, knight can reach 0
30        nextDp[0] = (dp[4] + dp[6]) % MOD;
31      
32        // From digits 6 and 8, knight can reach 1
33        nextDp[1] = (dp[6] + dp[8]) % MOD;
34      
35        // From digits 7 and 9, knight can reach 2
36        nextDp[2] = (dp[7] + dp[9]) % MOD;
37      
38        // From digits 4 and 8, knight can reach 3
39        nextDp[3] = (dp[4] + dp[8]) % MOD;
40      
41        // From digits 0, 3, and 9, knight can reach 4
42        nextDp[4] = (dp[0] + dp[3] + dp[9]) % MOD;
43      
44        // Digit 5 has no valid knight moves (stays at 0)
45      
46        // From digits 0, 1, and 7, knight can reach 6
47        nextDp[6] = (dp[0] + dp[1] + dp[7]) % MOD;
48      
49        // From digits 2 and 6, knight can reach 7
50        nextDp[7] = (dp[2] + dp[6]) % MOD;
51      
52        // From digits 1 and 3, knight can reach 8
53        nextDp[8] = (dp[1] + dp[3]) % MOD;
54      
55        // From digits 2 and 4, knight can reach 9
56        nextDp[9] = (dp[2] + dp[4]) % MOD;
57      
58        // Update dp array with new values
59        dp.splice(0, 10, ...nextDp);
60    }
61  
62    // Sum all possible ending positions and return modulo result
63    return dp.reduce((sum, count) => (sum + count) % MOD);
64}
65

Time and Space Complexity

The time complexity is O(n), where n is the input parameter representing the number of hops the knight makes. The algorithm uses a loop that iterates n - 1 times, and in each iteration, it performs a constant number of operations (calculating new values for 9 positions based on the previous values). Since each iteration takes O(1) time and we have n - 1 iterations, the overall time complexity is O(n).

The space complexity is O(1) or equivalently O(|Σ|) where |Σ| = 10, representing the number of digits on the dial pad. The algorithm uses two arrays f and g, each of fixed size 10, to store the number of ways to reach each digit. Since the size of these arrays is constant and doesn't depend on the input n, the space complexity is O(1). In the context of the problem, this can also be expressed as O(|Σ|) where Σ is the set of digits {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, making |Σ| = 10.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Forgetting to Apply Modulo During Intermediate Calculations

The Pitfall: The current solution only applies modulo at the very end when returning the result. For large values of n, the intermediate sums can overflow, causing incorrect results even though we're using Python which handles big integers. More importantly, in languages with fixed integer sizes (like Java or C++), this would cause integer overflow.

Why It Happens: Each iteration potentially multiplies the values by a factor related to the number of valid moves. After many iterations, these values grow exponentially large, which can lead to:

  • Performance degradation due to handling very large numbers
  • Integer overflow in languages with fixed-size integers
  • Potential numerical instability

The Fix: Apply modulo operation after each calculation to keep numbers manageable:

class Solution:
    def knightDialer(self, n: int) -> int:
        MOD = 10**9 + 7
        current_count = [1] * 10
      
        for _ in range(n - 1):
            next_count = [0] * 10
          
            # Apply modulo after each calculation
            next_count[0] = (current_count[4] + current_count[6]) % MOD
            next_count[1] = (current_count[6] + current_count[8]) % MOD
            next_count[2] = (current_count[7] + current_count[9]) % MOD
            next_count[3] = (current_count[4] + current_count[8]) % MOD
            next_count[4] = (current_count[0] + current_count[3] + current_count[9]) % MOD
            next_count[6] = (current_count[0] + current_count[1] + current_count[7]) % MOD
            next_count[7] = (current_count[2] + current_count[6]) % MOD
            next_count[8] = (current_count[1] + current_count[3]) % MOD
            next_count[9] = (current_count[2] + current_count[4]) % MOD
          
            current_count = next_count
      
        return sum(current_count) % MOD

2. Misunderstanding the Problem: Confusing Number of Jumps with Number Length

The Pitfall: A common mistake is iterating n times instead of n-1 times. The problem asks for phone numbers of length n, which means:

  • Starting position counts as the first digit (no jump needed)
  • To create an n-digit number, we need exactly n-1 jumps

Example:

  • For n=1: No jumps needed, we can start at any of the 10 digits → answer is 10
  • For n=2: Need 1 jump from starting position → creates 2-digit numbers

The Fix: Always iterate n-1 times, not n times. The current solution correctly does this with for _ in range(n - 1).

3. Not Handling Edge Case n=1

The Pitfall: When n=1, the loop doesn't execute at all (since range(0) is empty), and we return the sum of the initial array, which is 10. While this is correct, it's important to understand why and potentially add explicit handling for clarity.

The Fix: Consider adding an explicit check for better code readability:

def knightDialer(self, n: int) -> int:
    if n == 1:
        return 10  # Can start at any digit for single-digit numbers
  
    MOD = 10**9 + 7
    # ... rest of the solution
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

How does quick sort divide the problem into subproblems?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More