Before attempting this problem, you should be comfortable with:
A knight on a phone dial pad can only jump to specific digits based on its L-shaped movement. For example, from digit 1, the knight can reach 6 or 8. We can precompute these valid jumps for each digit. To count all n-digit numbers, we try starting from each digit and recursively count how many paths of length n exist. This explores all possibilities but leads to massive redundant computation.
jumps array where jumps[d] lists all digits reachable from digit d.n == 1, return 10 since any single digit is valid.dfs(n, d) that returns the count of numbers with n remaining digits starting from digit d.n == 0, return 1 (found one valid number).dfs(n - 1, next) for each next in jumps[d].dfs(n - 1, d) for each starting digit d from 0 to 9 and sum the results.class Solution:
def knightDialer(self, n: int) -> int:
if n == 1:
return 10
mod = 1000000007
jumps = [
[4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9],
[], [0, 1, 7], [2, 6], [1, 3], [2, 4]
]
def dfs(n, d):
if n == 0:
return 1
res = 0
for j in jumps[d]:
res = (res + dfs(n - 1, j)) % mod
return res
res = 0
for d in range(10):
res = (res + dfs(n - 1, d)) % mod
return resThe recursive solution recalculates the same subproblems many times. For instance, the count of paths from digit 4 with 5 remaining steps is computed repeatedly. By memoizing results in a 2D array indexed by (digit, remaining steps), we avoid redundant work. Each unique state is computed only once.
dp[digit][remaining_steps] initialized to -1.dfs(n, d), check if dp[d][n] is already computed; if so, return it.dp[d][n].class Solution:
def knightDialer(self, n: int) -> int:
if n == 1:
return 10
mod = 1000000007
jumps = [
[4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9],
[], [0, 1, 7], [2, 6], [1, 3], [2, 4]
]
dp = [[-1] * (n + 1) for _ in range(10)]
def dfs(n, d):
if n == 0:
return 1
if dp[d][n] != -1:
return dp[d][n]
dp[d][n] = 0
for j in jumps[d]:
dp[d][n] = (dp[d][n] + dfs(n - 1, j)) % mod
return dp[d][n]
res = 0
for d in range(10):
res = (res + dfs(n - 1, d)) % mod
return resInstead of recursing from the top, we can build up from the base case. Starting with step 0 (each digit has exactly 1 way to form a 1-digit number), we iteratively compute the counts for step 1, step 2, and so on up to step n-1. At each step, the count for a digit equals the sum of counts from all digits that can jump to it.
dp[d][0] = 1 for all digits (base case).1 to n-1:d, sum up dp[j][step-1] for all j in jumps[d].dp[d][n-1] for all digits d.class Solution:
def knightDialer(self, n: int) -> int:
if n == 1:
return 10
mod = 1000000007
jumps = [
[4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9],
[], [0, 1, 7], [2, 6], [1, 3], [2, 4]
]
dp = [[0] * (n + 1) for _ in range(10)]
for d in range(10):
dp[d][0] = 1
for step in range(1, n):
for d in range(10):
dp[d][step] = sum(dp[j][step - 1] for j in jumps[d]) % mod
return sum(dp[d][n - 1] for d in range(10)) % modSince each step only depends on the previous step, we don't need to store the entire DP table. We can use two 1D arrays (or swap between them) to track counts for the current and previous steps. This reduces space from O(10 * n) to O(10), which is effectively O(1).
dp array with 1 for each digit.nextDp array initialized to 0.d, add dp[d] to nextDp[j] for each j in jumps[d].dp with nextDp.dp for the final answer.class Solution:
def knightDialer(self, n: int) -> int:
if n == 1:
return 10
mod = 1000000007
jumps = [
[4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9],
[], [0, 1, 7], [2, 6], [1, 3], [2, 4]
]
dp = [1] * 10
for step in range(n - 1):
next_dp = [0] * 10
for d in range(10):
for j in jumps[d]:
next_dp[j] = (next_dp[j] + dp[d]) % mod
dp = next_dp
res = 0
for d in dp:
res = (res + d) % mod
return resWe can exploit the symmetry in the phone dial pad. Due to the knight's movement pattern, several digits behave identically: digits 1, 3, 7, 9 form one group (corners), digits 2 and 8 form another (top and bottom), digits 4 and 6 another (left and right), and digit 0 is alone. By grouping these, we reduce the state space to just 4 values, leading to constant-time transitions per step.
D (digit 5 area), A (corners 1,3,7,9), B (top/bottom 2,8), C (sides 4,6).[1, 4, 2, 2] for [D, A, B, C].class Solution:
def knightDialer(self, n: int) -> int:
if n == 1:
return 10
mod = 10**9 + 7
jumps = [1, 4, 2, 2] # [D, A, B, C]
for _ in range(n - 1):
tmp = [0] * 4
tmp[0] = jumps[3]
tmp[1] = 2 * jumps[2] + 2 * jumps[3]
tmp[2] = jumps[1]
tmp[3] = 2 * jumps[0] + jumps[1]
jumps = tmp
return sum(jumps) % modThe transition from one step to the next can be represented as a matrix multiplication. If we encode the adjacency of knight jumps in a 10x10 matrix, then raising this matrix to the power n-1 gives us the count of paths of length n. Matrix exponentiation allows us to compute the result in O(log n) time, making this approach extremely efficient for very large n.
mat[i][j] = 1 if a knight can jump from digit i to digit j.mat^(n-1).n-digit numbers.class Matrix:
def __init__(self, size):
self.n = size
self.a = [[0] * size for _ in range(size)]
def __mul__(self, other):
n = self.n
MOD = 10**9 + 7
product = Matrix(n)
for i in range(n):
for j in range(n):
for k in range(n):
product.a[i][k] = (product.a[i][k] + self.a[i][j] * other.a[j][k]) % MOD
return product
def matpow(mat, n, size):
res = Matrix(size)
for i in range(size):
res.a[i][i] = 1 # Identity matrix
while n:
if n & 1:
res = res * mat
mat = mat * mat
n >>= 1
return res
class Solution:
def knightDialer(self, n: int) -> int:
if n == 1:
return 10
MOD = 10**9 + 7
jumps = [
[4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9],
[], [0, 1, 7], [2, 6], [1, 3], [2, 4]
]
mat = Matrix(10)
for i in range(10):
for j in jumps[i]:
mat.a[i][j] = 1
res = matpow(mat, n - 1, 10)
ans = sum(sum(res.a[i]) for i in range(10)) % MOD
return ansThe knight cannot jump to any digit from position 5 on the phone dial pad. A common mistake is either forgetting to include digit 5 in the initial count (for n=1) or incorrectly assuming it contributes to longer sequences. For n >= 2, digit 5 effectively becomes a dead end and contributes nothing to the count.
The number of valid phone numbers grows exponentially and quickly exceeds 32-bit integer limits. Failing to apply the modulo operation (10^9 + 7) after each addition leads to overflow. Apply modulo at every step where values are accumulated, not just at the final result.
The knight's L-shaped movement on a phone dial creates a specific jump pattern that must be encoded correctly. Common mistakes include forgetting certain jumps (like 0 can reach 4 and 6) or adding invalid jumps (like 5 reaching any digit, or digits jumping to themselves). Double-check the adjacency list against the actual phone pad layout.