Jump Game VII
Difficulty: Medium Source: NeetCode
Problem
You are given a binary string
s(only'0'and'1'), and two integersminJumpandmaxJump. Start at index0(which is always'0'). On each move, jump from indexito any indexjwherei + minJump <= j <= i + maxJumpands[j] == '0'. Returntrueif you can reach indexn - 1.Example 1: Input:
s = "011010",minJump = 2,maxJump = 3Output:trueExplanation: Jump from index 0 → 3 → 5.Example 2: Input:
s = "01101110",minJump = 2,maxJump = 3Output:falseConstraints:
2 <= s.length <= 10^5s[0] == '0'1 <= minJump <= maxJump < s.length
Prerequisites
Before attempting this problem, you should be comfortable with:
- Jump Game I & II — simpler jump variants
- Prefix sums — counting elements in a range efficiently
- Sliding window — maintaining a running count over a window
1. Brute Force / BFS
Intuition
Do a BFS from index 0. At each reachable index i that contains '0', add all indices j in [i + minJump, i + maxJump] that also contain '0' and haven’t been visited. Return true if we ever reach n - 1.
Algorithm
- Start BFS from
{0}. - For each reachable index
i, explorejfromi + minJumptoi + maxJump. - Add unvisited
'0'indices to the next level. - Return
Trueifn-1is reached.
Solution
from collections import deque
def canReach(s, minJump, maxJump):
n = len(s)
if s[-1] == '1':
return False
visited = {0}
queue = deque([0])
while queue:
i = queue.popleft()
for j in range(i + minJump, min(i + maxJump + 1, n)):
if j not in visited and s[j] == '0':
if j == n - 1:
return True
visited.add(j)
queue.append(j)
return (n - 1) in visited
print(canReach("011010", 2, 3)) # True
print(canReach("01101110", 2, 3)) # False
print(canReach("00", 1, 1)) # True
Complexity
- Time:
O(n * maxJump)— slow when window is large - Space:
O(n)
2. Greedy — Sliding Window with Prefix Reachability
Intuition
Use a boolean array reachable[i] = can we reach index i. reachable[0] = True. For each index j (where s[j] == '0'), it’s reachable if any index in [j - maxJump, j - minJump] is reachable.
Naively checking this range is O(n * maxJump). To speed it up, maintain a running count of reachable indices in a sliding window. Use a prefix sum (or an integer counter updated as the window slides).
Algorithm
reachable = [False] * n,reachable[0] = True.window_count = 0— count of reachable positions in the current window.- For
jfrom1ton-1:- If
j - minJump >= 0andreachable[j - minJump]: incrementwindow_count. - If
j - maxJump - 1 >= 0andreachable[j - maxJump - 1]: decrementwindow_count. - If
s[j] == '0'andwindow_count > 0:reachable[j] = True.
- If
- Return
reachable[n-1].
Solution
def canReach(s, minJump, maxJump):
n = len(s)
if s[-1] == '1':
return False
reachable = [False] * n
reachable[0] = True
window_count = 0 # reachable positions in [j - maxJump, j - minJump]
for j in range(1, n):
# Add left boundary entering the window
if j >= minJump and reachable[j - minJump]:
window_count += 1
# Remove element leaving the window from the right
if j > maxJump and reachable[j - maxJump - 1]:
window_count -= 1
# Mark j reachable if it's '0' and there's a valid predecessor
if s[j] == '0' and window_count > 0:
reachable[j] = True
return reachable[n - 1]
print(canReach("011010", 2, 3)) # True
print(canReach("01101110", 2, 3)) # False
print(canReach("00", 1, 1)) # True
print(canReach("0000000000", 2, 3)) # True
# Alternatively, using prefix sums
def canReach_prefix(s, minJump, maxJump):
n = len(s)
if s[-1] == '1':
return False
reachable = [0] * (n + 1) # prefix sum of reachable flags
reachable[1] = 1 # index 0 is reachable (1-indexed prefix)
dp = [False] * n
dp[0] = True
for j in range(1, n):
if s[j] == '0':
lo = max(0, j - maxJump)
hi = j - minJump
if hi >= 0 and reachable[hi + 1] - reachable[lo] > 0:
dp[j] = True
reachable[j + 1] = reachable[j] + (1 if dp[j] else 0)
return dp[n - 1]
print(canReach_prefix("011010", 2, 3)) # True
print(canReach_prefix("01101110", 2, 3)) # False
Complexity
- Time:
O(n) - Space:
O(n)
Common Pitfalls
Sliding the window incorrectly. The window for position j is [j - maxJump, j - minJump]. When we process j, we add the incoming element j - minJump and remove the outgoing element j - maxJump - 1. Off-by-one here is very easy.
Not checking s[-1] == '1' early. If the last position is '1', we can never land there — short-circuit immediately.
Using BFS when maxJump is large. The BFS approach is O(n * maxJump), which can be O(n²) in the worst case. The sliding window / prefix sum approach is strictly O(n).