Number of Ways to Stay in the Same Place After Some Steps
Hard · 1-D Dynamic Programming · Explanation and code example
Dynamic Programming (Optimal)
How it works
We can push the space optimization even further. Instead of using two separate arrays, we can update the DP array in place if we are careful about the order of updates.
The trick is to process positions from left to right while keeping track of the previous value before it gets overwritten. This way, when we update dp[i], we still have access to the old dp[i-1] (saved in a temporary variable) and the old dp[i+1] (not yet updated).
Steps
- Use a single 1D array
dpwheredp[i]represents ways to reach positioni. - Initialize
dp[0] = 1. - For each step:
- Track
prevto store the old value ofdp[i-1]before it was updated. - For each position
ifrom0toarrLen - 1:- Save the current
dp[i]ascurbefore modifying it. - Add
prev(contribution from the left) ifi > 0. - Add
dp[i+1](contribution from the right) ifi < arrLen - 1. - Update
prev = curfor the next iteration.
- Save the current
- Track
- Return
dp[0].
Complexity
- Time complexity:
O(n * min(n, m)) - Space complexity:
O(min(n, m))
Where
nis the number of steps andmis the size of the array.
Things to watch for
Not Bounding the Maximum Reachable Position
Without optimization, the DP table would be O(steps * arrLen) which can be prohibitively large when arrLen is huge but steps is small. The key insight is that you can never move more than steps positions to the right (since you need steps to return). Always limit the effective array length to min(steps, arrLen) to avoid TLE or memory issues.
Incorrect Boundary Checks for Movement
When at position i, moving left requires i > 0 and moving right requires i < arrLen - 1. A common mistake is using i < arrLen for the right boundary (allowing out-of-bounds movement) or forgetting the left boundary check entirely. These off-by-one errors cause incorrect results or runtime errors.
Mixing Up the DP Transition Direction
In bottom-up DP, you need to carefully track whether dp[step][i] represents the number of ways to reach position i after step moves (forward direction) or the number of ways to return to position 0 from position i with step moves remaining (backward direction). Mixing these up leads to incorrect recurrence relations and wrong answers.
Python code example
class Solution:
def numWays(self, steps: int, arrLen: int) -> int:
mod = 10**9 + 7
arrLen = min(steps, arrLen)
dp = [0] * arrLen
dp[0] = 1
for _ in range(steps):
prev = 0
for i in range(arrLen):
cur = dp[i]
if i > 0:
dp[i] = (dp[i] + prev) % mod
if i < arrLen - 1:
dp[i] = (dp[i] + dp[i + 1]) % mod
prev = cur
return dp[0]
C++ code example
class Solution {
public:
int numWays(int steps, int arrLen) {
const int MOD = 1e9 + 7;
arrLen = min(steps, arrLen);
vector<int> dp(arrLen, 0);
dp[0] = 1;
for (int step = 0; step < steps; step++) {
int prev = 0;
for (int i = 0; i < arrLen; i++) {
int cur = dp[i];
if (i > 0) {
dp[i] = (dp[i] + prev) % MOD;
}
if (i < arrLen - 1) {
dp[i] = (dp[i] + dp[i + 1]) % MOD;
}
prev = cur;
}
}
return dp[0];
}
};
Source and license
Adapted from NeetCode’s source · MIT license. Node types are supplied by LeetCode where needed.