Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1269. Number of Ways to Stay in the Same Place After Some Steps - Leetcode Solution

Problem Description

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.

Thought Process

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).

Solution Approach

We will use dynamic programming to solve this problem efficiently. Here is a step-by-step breakdown:

  1. State Definition: Let dp[i][j] represent the number of ways to reach position j after i steps.
  2. Initialization: At step 0, we are only at position 0: dp[0][0] = 1. All other positions are 0.
  3. Transition: For each step and each position, the number of ways to reach a position is the sum of the ways to:
    • Stay at the same position
    • Move from the left (if possible)
    • Move from the right (if possible)
    For position 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)
  4. Optimization: Since at each step we only need the previous row, we can use two 1-D arrays and swap them for each iteration, reducing space usage.
  5. Result: After steps moves, dp[steps][0] gives the number of ways to be at position 0.
  6. Modulo Operation: Since the answer can be large, we take modulo 10^9 + 7 at each step.

Example Walkthrough

Suppose steps = 3 and arrLen = 2.
The possible positions are 0 and 1.
Let's build up the DP table step by step:

  • Step 0: dp[0][0] = 1 (start at 0)
  • Step 1:
    • At 0: stay (from 0), or move from 1 (not possible yet): dp[1][0] = dp[0][0] + dp[0][1] = 1 + 0 = 1
    • At 1: move from 0: dp[1][1] = dp[0][0] = 1
  • Step 2:
    • At 0: stay (from 0), move from 1: dp[2][0] = dp[1][0] + dp[1][1] = 1 + 1 = 2
    • At 1: stay (from 1), move from 0: dp[2][1] = dp[1][1] + dp[1][0] = 1 + 1 = 2
  • Step 3:
    • At 0: stay (from 0), move from 1: dp[3][0] = dp[2][0] + dp[2][1] = 2 + 2 = 4
    • At 1: stay (from 1), move from 0: dp[3][1] = dp[2][1] + dp[2][0] = 2 + 2 = 4
But we must check the transitions carefully: at each step, for position 1, you can only come from 0 or stay at 1, but can't move to 2 (since arrLen = 2).
After 3 steps, dp[3][0] = 4. There are 4 ways to stay at 0 after 3 steps.

Time and Space Complexity

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:

  • Let maxPos = min(steps, arrLen - 1).
  • For each of the steps steps, we process up to maxPos + 1 positions.
  • Time Complexity: O(steps * maxPos)
  • Space Complexity: O(maxPos) (since we only keep two rows at a time)
This is efficient and works for the problem's constraints.

Summary

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.

Code Implementation

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];
};