1871. Jump Game VII
Problem Description
You are given a binary string s
(containing only '0's and '1's) where you start at index 0. The character at index 0 is always '0'. Your goal is to reach the last index of the string by jumping.
The jumping rules are:
- From any position
i
, you can jump to positionj
if:- The jump distance is within the allowed range:
i + minJump <= j <= min(i + maxJump, s.length - 1)
- The destination position has a '0':
s[j] == '0'
- The jump distance is within the allowed range:
You need to determine if it's possible to reach the last index of the string following these rules.
For example, if you have s = "011010"
, minJump = 2
, and maxJump = 3
:
- From index 0, you can jump to indices 2 or 3 (within range [0+2, 0+3])
- Index 2 has '1', so you can't land there
- Index 3 has '0', so you can jump there
- From index 3, you can jump to index 5 (within range [3+2, 3+3])
- Index 5 has '0', so you can reach it
- Therefore, the answer would be
true
The function should return true
if you can reach the last index, and false
otherwise.
Intuition
The key insight is that this is a reachability problem where we need to determine if each position can be reached from previously reachable positions. We can think of it as building a path from index 0 to the last index, where each step must satisfy the jumping constraints.
The natural approach is dynamic programming - for each position i
, we check if it can be reached from any valid previous position. A position i
is reachable if:
- The character at position
i
is '0' (we can only land on '0's) - There exists at least one reachable position
j
from which we can jump toi
For the second condition, position j
must satisfy: i - maxJump <= j <= i - minJump
. This means we need to check if there's any reachable position within this range.
Here's where the optimization comes in: instead of checking each position in the range individually (which would be O(maxJump - minJump) for each position), we can use a prefix sum array to answer "is there any reachable position in range [l, r]?" in O(1) time.
The prefix sum array pre[i]
stores the count of reachable positions in the first i
positions. If pre[r+1] - pre[l] > 0
, it means there's at least one reachable position in the range [l, r]
. This transforms our problem from O(n * (maxJump - minJump)) to O(n) time complexity.
We build the solution incrementally: starting from index 0 (which is always reachable), we determine the reachability of each subsequent position based on whether we can jump to it from any previously reached position. The prefix sum array helps us efficiently check if any valid jumping position exists within the required range.
Learn more about Dynamic Programming, Prefix Sum and Sliding Window patterns.
Solution Approach
We implement the solution using prefix sum combined with dynamic programming:
Data Structures:
pre
: A prefix sum array of lengthn+1
wherepre[i]
represents the count of reachable positions in the firsti
positionsf
: A boolean array of lengthn
wheref[i]
indicates whether positioni
is reachable
Initialization:
pre[1] = 1
: Since we start at index 0, the first position is reachablef[0] = True
: Position 0 is our starting point- All other positions in
f
are initiallyFalse
Algorithm Steps:
-
Iterate through each position
i
from 1 ton-1
: -
Check if
s[i] == '0'
(we can only land on '0's):- If it's '0', determine if we can reach position
i
from any valid previous position - Calculate the valid jumping range: positions from which we can jump to
i
- Left boundary:
l = max(0, i - maxJump)
(can't go below index 0) - Right boundary:
r = i - minJump
- Left boundary:
- If it's '0', determine if we can reach position
-
Check reachability using prefix sum:
- If
l <= r
(valid range exists) andpre[r+1] - pre[l] > 0
(at least one reachable position exists in range) - Then
f[i] = True
, otherwisef[i] = False
- If
-
Update the prefix sum array:
pre[i+1] = pre[i] + f[i]
- This adds 1 to the count if position
i
is reachable, otherwise adds 0
-
Return
f[-1]
(the reachability of the last position)
Time Complexity: O(n) - We iterate through the string once, and each position requires O(1) operations thanks to the prefix sum optimization.
Space Complexity: O(n) - We use two arrays of size approximately n.
The elegance of this solution lies in using the prefix sum to convert range queries ("is there any reachable position in range [l, r]?") from O(range_size) to O(1), making the overall algorithm linear time.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with s = "0110100"
, minJump = 2
, and maxJump = 3
.
Initial Setup:
n = 7
(length of string)f = [True, False, False, False, False, False, False]
(only position 0 is initially reachable)pre = [0, 1, 0, 0, 0, 0, 0, 0]
(prefix sum array,pre[1] = 1
since position 0 is reachable)
Step-by-step iteration:
i = 1: s[1] = '1'
- Cannot land on '1', so
f[1] = False
- Update:
pre[2] = pre[1] + 0 = 1
i = 2: s[2] = '1'
- Cannot land on '1', so
f[2] = False
- Update:
pre[3] = pre[2] + 0 = 1
i = 3: s[3] = '0'
- Can potentially land here since it's '0'
- Calculate jump range: who can jump to position 3?
l = max(0, 3 - 3) = 0
(furthest position that can reach here)r = 3 - 2 = 1
(closest position that can reach here)
- Check if any position in range [0, 1] is reachable:
pre[2] - pre[0] = 1 - 0 = 1 > 0
✓- Position 0 is reachable and can jump to position 3!
- Set
f[3] = True
- Update:
pre[4] = pre[3] + 1 = 2
i = 4: s[4] = '1'
- Cannot land on '1', so
f[4] = False
- Update:
pre[5] = pre[4] + 0 = 2
i = 5: s[5] = '0'
- Can potentially land here since it's '0'
- Calculate jump range:
l = max(0, 5 - 3) = 2
r = 5 - 2 = 3
- Check if any position in range [2, 3] is reachable:
pre[4] - pre[2] = 2 - 1 = 1 > 0
✓- Position 3 is reachable and can jump to position 5!
- Set
f[5] = True
- Update:
pre[6] = pre[5] + 1 = 3
i = 6: s[6] = '0'
- Can potentially land here since it's '0'
- Calculate jump range:
l = max(0, 6 - 3) = 3
r = 6 - 2 = 4
- Check if any position in range [3, 4] is reachable:
pre[5] - pre[3] = 2 - 1 = 1 > 0
✓- Position 3 is reachable and can jump to position 6!
- Set
f[6] = True
- Update:
pre[7] = pre[6] + 1 = 4
Final Result:
f[6] = True
, so we can reach the last index- The path taken: 0 → 3 → 6
The prefix sum optimization allows us to check "is any position in range [l, r] reachable?" in O(1) time by computing pre[r+1] - pre[l]
. If this value is greater than 0, at least one position in that range is reachable.
Solution Implementation
1class Solution:
2 def canReach(self, s: str, minJump: int, maxJump: int) -> bool:
3 n = len(s)
4
5 # prefix_sum[i] stores the count of reachable positions from 0 to i-1
6 prefix_sum = [0] * (n + 1)
7 prefix_sum[1] = 1 # Position 0 is reachable (starting position)
8
9 # reachable[i] indicates whether position i is reachable from position 0
10 reachable = [True] + [False] * (n - 1) # Only position 0 is initially reachable
11
12 # Process each position from left to right
13 for i in range(1, n):
14 # Can only land on positions with '0'
15 if s[i] == "0":
16 # Calculate the range of positions we can jump from to reach position i
17 # We can reach i from positions [i - maxJump, i - minJump]
18 left_bound = max(0, i - maxJump)
19 right_bound = i - minJump
20
21 # Check if there's at least one reachable position in the valid range
22 # Using prefix sum to check if any position in [left_bound, right_bound] is reachable
23 if left_bound <= right_bound:
24 count_reachable = prefix_sum[right_bound + 1] - prefix_sum[left_bound]
25 reachable[i] = count_reachable > 0
26
27 # Update prefix sum for the next iteration
28 prefix_sum[i + 1] = prefix_sum[i] + (1 if reachable[i] else 0)
29
30 # Return whether the last position is reachable
31 return reachable[-1]
32
1class Solution {
2 public boolean canReach(String s, int minJump, int maxJump) {
3 int n = s.length();
4
5 // Prefix sum array to track count of reachable positions
6 // prefixSum[i] stores the count of reachable positions from index 0 to i-1
7 int[] prefixSum = new int[n + 1];
8 prefixSum[1] = 1; // Position 0 is reachable (starting position)
9
10 // Dynamic programming array to track if position i is reachable
11 boolean[] isReachable = new boolean[n];
12 isReachable[0] = true; // Starting position is always reachable
13
14 // Iterate through each position in the string
15 for (int i = 1; i < n; i++) {
16 // Can only jump to positions with '0'
17 if (s.charAt(i) == '0') {
18 // Calculate the valid range of positions we can jump from
19 // We can jump from position j to i if: i - maxJump <= j <= i - minJump
20 int leftBound = Math.max(0, i - maxJump);
21 int rightBound = i - minJump;
22
23 // Check if there exists at least one reachable position in the range [leftBound, rightBound]
24 // Using prefix sum to efficiently check if any position in range is reachable
25 isReachable[i] = leftBound <= rightBound &&
26 prefixSum[rightBound + 1] - prefixSum[leftBound] > 0;
27 }
28
29 // Update prefix sum array
30 // Add 1 if current position is reachable, otherwise add 0
31 prefixSum[i + 1] = prefixSum[i] + (isReachable[i] ? 1 : 0);
32 }
33
34 // Return whether the last position is reachable
35 return isReachable[n - 1];
36 }
37}
38
1class Solution {
2public:
3 bool canReach(string s, int minJump, int maxJump) {
4 int n = s.size();
5
6 // Prefix sum array to track count of reachable positions up to index i
7 // prefixSum[i] represents the number of reachable positions in range [0, i-1]
8 int prefixSum[n + 1];
9 memset(prefixSum, 0, sizeof(prefixSum));
10 prefixSum[1] = 1; // Position 0 is reachable (base case)
11
12 // Dynamic programming array where canReach[i] indicates if position i is reachable
13 bool canReachPosition[n];
14 memset(canReachPosition, 0, sizeof(canReachPosition));
15 canReachPosition[0] = true; // Starting position is always reachable
16
17 // Iterate through each position in the string
18 for (int i = 1; i < n; ++i) {
19 // Only consider positions with '0' (valid landing positions)
20 if (s[i] == '0') {
21 // Calculate the valid range of positions that can jump to position i
22 // leftBound: furthest position that can reach i (at most maxJump away)
23 int leftBound = max(0, i - maxJump);
24 // rightBound: closest position that can reach i (at least minJump away)
25 int rightBound = i - minJump;
26
27 // Check if there exists at least one reachable position in the valid range
28 // Using prefix sum for O(1) range query
29 canReachPosition[i] = (leftBound <= rightBound) &&
30 (prefixSum[rightBound + 1] - prefixSum[leftBound] > 0);
31 }
32
33 // Update prefix sum with current position's reachability
34 prefixSum[i + 1] = prefixSum[i] + canReachPosition[i];
35 }
36
37 // Return whether the last position is reachable
38 return canReachPosition[n - 1];
39 }
40};
41
1/**
2 * Determines if it's possible to reach the last index of the string
3 * by jumping between positions containing '0'.
4 *
5 * @param s - Binary string where '0' represents valid positions to land on
6 * @param minJump - Minimum jump distance allowed
7 * @param maxJump - Maximum jump distance allowed
8 * @returns true if the last position can be reached, false otherwise
9 */
10function canReach(s: string, minJump: number, maxJump: number): boolean {
11 const stringLength: number = s.length;
12
13 // Prefix sum array to track count of reachable positions up to index i
14 // prefixSum[i] stores the count of reachable positions from index 0 to i-1
15 const prefixSum: number[] = Array(stringLength + 1).fill(0);
16 prefixSum[1] = 1; // Position 0 is always reachable (starting position)
17
18 // Dynamic programming array where isReachable[i] indicates if position i can be reached
19 const isReachable: boolean[] = Array(stringLength).fill(false);
20 isReachable[0] = true; // Starting position is always reachable
21
22 // Iterate through each position in the string
23 for (let currentIndex = 1; currentIndex < stringLength; ++currentIndex) {
24 // Check if current position is valid to land on (contains '0')
25 if (s[currentIndex] === '0') {
26 // Calculate the range of positions we can jump from to reach currentIndex
27 // leftBound: furthest position we can jump from (maxJump distance back)
28 // rightBound: closest position we can jump from (minJump distance back)
29 const leftBound: number = Math.max(0, currentIndex - maxJump);
30 const rightBound: number = currentIndex - minJump;
31
32 // Check if there exists at least one reachable position in the valid jump range
33 // Using prefix sum to efficiently check if any position in [leftBound, rightBound] is reachable
34 isReachable[currentIndex] = leftBound <= rightBound &&
35 prefixSum[rightBound + 1] - prefixSum[leftBound] > 0;
36 }
37
38 // Update prefix sum array with current position's reachability status
39 prefixSum[currentIndex + 1] = prefixSum[currentIndex] + (isReachable[currentIndex] ? 1 : 0);
40 }
41
42 // Return whether the last position is reachable
43 return isReachable[stringLength - 1];
44}
45
Time and Space Complexity
Time Complexity: O(n)
The algorithm iterates through the string once with a single for loop from index 1 to n-1. Within each iteration:
- Calculating
max(0, i - maxJump)
andi - minJump
takesO(1)
time - Checking if
s[i] == "0"
takesO(1)
time - Computing
pre[r + 1] - pre[l]
takesO(1)
time since we're accessing precomputed prefix sum values - Updating
pre[i + 1]
takesO(1)
time
Since we perform O(1)
operations for each of the n
positions, the total time complexity is O(n)
.
Space Complexity: O(n)
The algorithm uses:
- Array
pre
of sizen + 1
to store prefix sums:O(n)
space - Array
f
of sizen
to store reachability status:O(n)
space - A few integer variables (
n
,i
,l
,r
):O(1)
space
The total space complexity is O(n) + O(n) + O(1) = O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Range Calculation for Jump Sources
Pitfall: A common mistake is incorrectly calculating which positions can jump TO position i
, leading to checking the wrong range of source positions.
Incorrect approach:
# Wrong: This calculates where we can jump TO from position i
left_bound = i + minJump
right_bound = min(i + maxJump, n - 1)
Correct approach:
# Correct: Calculate which positions can jump FROM to reach position i
left_bound = max(0, i - maxJump) # Can't go below index 0
right_bound = i - minJump # Must jump at least minJump distance
The key insight is that when processing position i
, we need to look BACKWARD to find positions that can reach i
, not forward.
2. Off-by-One Errors in Prefix Sum Indexing
Pitfall: Misaligning the prefix sum array indices with the actual position indices, causing incorrect range sum calculations.
Incorrect approach:
# Wrong: Inconsistent indexing count_reachable = prefix_sum[right_bound] - prefix_sum[left_bound - 1] # This fails when left_bound = 0 (index out of bounds)
Correct approach:
# Correct: Consistent 1-indexed prefix sum prefix_sum = [0] * (n + 1) # Size n+1 for 1-indexed prefix sum count_reachable = prefix_sum[right_bound + 1] - prefix_sum[left_bound]
3. Forgetting to Check Valid Range Bounds
Pitfall: Not verifying that the calculated range [left_bound, right_bound]
is valid before using it.
Incorrect approach:
# Wrong: Doesn't check if range is valid count_reachable = prefix_sum[right_bound + 1] - prefix_sum[left_bound] reachable[i] = count_reachable > 0
Correct approach:
# Correct: Check range validity first if left_bound <= right_bound: count_reachable = prefix_sum[right_bound + 1] - prefix_sum[left_bound] reachable[i] = count_reachable > 0 # If left_bound > right_bound, no valid source positions exist
This is crucial when i < minJump
, where right_bound = i - minJump
becomes negative, making the range invalid.
4. Not Handling Edge Cases Properly
Pitfall: Failing to handle special cases like when the target is already at position 0 or when minJump is larger than the string length.
Solution: Always verify:
- The starting position (index 0) has '0' (given in problem)
- The last position has '0' (otherwise it's unreachable regardless)
- Early return if
n - 1 < minJump
(impossible to reach the end)
# Add early termination checks
if s[-1] == '1':
return False # Can't land on '1'
if len(s) == 1:
return True # Already at the end
if minJump >= len(s):
return False # Can't reach the end with minimum jump
You are given an array of intervals where intervals[i] = [start_i, end_i]
represent the start and end of the ith
interval. You need to merge all overlapping intervals and return an array of the non-overlapping intervals that cover all the intervals in the input.
Recommended Readings
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
https assets algo monster cover_photos stack svg Sliding Window Maximum Monotonic Stack We have an array and a sliding window defined by a start index and an end index The sliding window moves from left of the array to right There are always k elements in the window The window
Want a Structured Path to Master System Design Too? Don’t Miss This!