You are standing at position 0
on an infinite number line. On each move, you can either move one step to the left, one step to the right, or stay in the same place. Given two integers, steps
(the total number of moves you must make) and arrLen
(the length of the array, which restricts your movement to positions 0
through arrLen - 1
), return the number of different ways you can stay at position 0
after exactly steps
moves.
Note that you cannot move outside the range [0, arrLen - 1]
at any time. The answer may be large, so return it modulo 10^9 + 7
.
At first glance, this problem looks similar to a classic combinatorics or recursion problem, where you might try to simulate every possible sequence of moves. However, with potentially hundreds or thousands of steps, brute-forcing every path quickly becomes infeasible.
The key insight is that, at each move, your position only depends on where you were on the previous move. This suggests a dynamic programming approach, where we keep track of the number of ways to reach each position at each step, reusing results instead of recomputing them.
We also notice that you can never get farther from position 0
than steps
itself, and you are also limited by arrLen
. This means we only need to consider positions up to min(steps, arrLen - 1)
.
We will use dynamic programming to solve this problem efficiently. Here is a step-by-step breakdown:
dp[i][j]
represent the number of ways to reach position j
after i
steps.
0
, we are only at position 0
: dp[0][0] = 1
. All other positions are 0
.
j
at step i
:
dp[i][j] = dp[i-1][j] (stay) + dp[i-1][j-1] (left, if j > 0) + dp[i-1][j+1] (right, if j+1 < arrLen)
steps
moves, dp[steps][0]
gives the number of ways to be at position 0
.
10^9 + 7
at each step.
Suppose steps = 3
and arrLen = 2
.
The possible positions are 0
and 1
.
Let's build up the DP table step by step:
dp[0][0] = 1
(start at 0)dp[1][0] = dp[0][0] + dp[0][1] = 1 + 0 = 1
dp[1][1] = dp[0][0] = 1
dp[2][0] = dp[1][0] + dp[1][1] = 1 + 1 = 2
dp[2][1] = dp[1][1] + dp[1][0] = 1 + 1 = 2
dp[3][0] = dp[2][0] + dp[2][1] = 2 + 2 = 4
dp[3][1] = dp[2][1] + dp[2][0] = 2 + 2 = 4
dp[3][0] = 4
. There are 4 ways to stay at 0 after 3 steps.
Brute-force: The naive approach would try every possible sequence of moves, leading to 3^steps
possibilities, which is exponential and infeasible for large steps
.
Optimized DP:
maxPos = min(steps, arrLen - 1)
.steps
steps, we process up to maxPos + 1
positions.O(steps * maxPos)
O(maxPos)
(since we only keep two rows at a time)In this problem, the main insight is recognizing the overlapping subproblems and optimal substructure, which makes dynamic programming the ideal solution. By carefully limiting the state space to only reachable positions and optimizing the DP to use rolling arrays, we achieve both time and space efficiency. The approach elegantly handles the constraints and demonstrates the power of DP in combinatorial path-counting problems.
class Solution:
def numWays(self, steps: int, arrLen: int) -> int:
MOD = 10 ** 9 + 7
maxPos = min(steps, arrLen - 1)
dp = [0] * (maxPos + 1)
dp[0] = 1
for i in range(1, steps + 1):
next_dp = [0] * (maxPos + 1)
for j in range(0, maxPos + 1):
next_dp[j] = dp[j]
if j - 1 >= 0:
next_dp[j] = (next_dp[j] + dp[j - 1]) % MOD
if j + 1 <= maxPos:
next_dp[j] = (next_dp[j] + dp[j + 1]) % MOD
dp = next_dp
return dp[0]
class Solution {
public:
int numWays(int steps, int arrLen) {
const int MOD = 1e9 + 7;
int maxPos = min(steps, arrLen - 1);
vector<int> dp(maxPos + 1, 0);
dp[0] = 1;
for (int i = 1; i <= steps; ++i) {
vector<int> next_dp(maxPos + 1, 0);
for (int j = 0; j <= maxPos; ++j) {
next_dp[j] = dp[j];
if (j - 1 >= 0)
next_dp[j] = (next_dp[j] + dp[j - 1]) % MOD;
if (j + 1 <= maxPos)
next_dp[j] = (next_dp[j] + dp[j + 1]) % MOD;
}
dp = next_dp;
}
return dp[0];
}
};
class Solution {
public int numWays(int steps, int arrLen) {
int MOD = 1000000007;
int maxPos = Math.min(steps, arrLen - 1);
int[] dp = new int[maxPos + 1];
dp[0] = 1;
for (int i = 1; i <= steps; i++) {
int[] nextDp = new int[maxPos + 1];
for (int j = 0; j <= maxPos; j++) {
nextDp[j] = dp[j];
if (j - 1 >= 0)
nextDp[j] = (nextDp[j] + dp[j - 1]) % MOD;
if (j + 1 <= maxPos)
nextDp[j] = (nextDp[j] + dp[j + 1]) % MOD;
}
dp = nextDp;
}
return dp[0];
}
}
var numWays = function(steps, arrLen) {
const MOD = 1e9 + 7;
const maxPos = Math.min(steps, arrLen - 1);
let dp = new Array(maxPos + 1).fill(0);
dp[0] = 1;
for (let i = 1; i <= steps; i++) {
let nextDp = new Array(maxPos + 1).fill(0);
for (let j = 0; j <= maxPos; j++) {
nextDp[j] = dp[j];
if (j - 1 >= 0)
nextDp[j] = (nextDp[j] + dp[j - 1]) % MOD;
if (j + 1 <= maxPos)
nextDp[j] = (nextDp[j] + dp[j + 1]) % MOD;
}
dp = nextDp;
}
return dp[0];
};