class Solution:
def canReach(self, s: str, minJump: int, maxJump: int) -> bool:
n = len(s)
if s[-1] != '0':
return False
dp = [False] * n
dp[0] = True
pre = 0
for i in range(1, n):
if i - minJump >= 0:
pre += dp[i - minJump]
if i - maxJump - 1 >= 0:
pre -= dp[i - maxJump - 1]
dp[i] = pre > 0 and s[i] == '0'
return dp[-1]
class Solution {
public:
bool canReach(string s, int minJump, int maxJump) {
int n = s.size();
if (s[n-1] != '0') return false;
vector<bool> dp(n, false);
dp[0] = true;
int pre = 0;
for (int i = 1; i < n; ++i) {
if (i - minJump >= 0) pre += dp[i - minJump];
if (i - maxJump - 1 >= 0) pre -= dp[i - maxJump - 1];
dp[i] = pre > 0 && s[i] == '0';
}
return dp[n-1];
}
};
class Solution {
public boolean canReach(String s, int minJump, int maxJump) {
int n = s.length();
if (s.charAt(n - 1) != '0') return false;
boolean[] dp = new boolean[n];
dp[0] = true;
int pre = 0;
for (int i = 1; i < n; i++) {
if (i - minJump >= 0) pre += dp[i - minJump] ? 1 : 0;
if (i - maxJump - 1 >= 0) pre -= dp[i - maxJump - 1] ? 1 : 0;
dp[i] = pre > 0 && s.charAt(i) == '0';
}
return dp[n - 1];
}
}
var canReach = function(s, minJump, maxJump) {
const n = s.length;
if (s[n-1] !== '0') return false;
const dp = new Array(n).fill(false);
dp[0] = true;
let pre = 0;
for (let i = 1; i < n; i++) {
if (i - minJump >= 0) pre += dp[i - minJump] ? 1 : 0;
if (i - maxJump - 1 >= 0) pre -= dp[i - maxJump - 1] ? 1 : 0;
dp[i] = pre > 0 && s[i] === '0';
}
return dp[n-1];
};
Given a binary string s (composed of '0's and '1's), and two integers minJump and maxJump, you start at index 0 and want to reach the last index. From position i, you can jump to any index j such that i + minJump ≤ j ≤ i + maxJump and s[j] == '0'. You cannot land on or jump over indices with '1'. Return true if you can reach the last index, otherwise return false.
At first, you might think of trying every possible jump from every '0' position, similar to a breadth-first search (BFS) or recursion. For each index, you could check all possible jumps in the range [minJump, maxJump] and see if you can eventually reach the end. However, this brute-force approach is inefficient for large strings because it would revisit the same positions multiple times, leading to a time complexity that is too high.
To optimize, we notice that we can use dynamic programming: for each position, we want to know if it's reachable from the start. But checking all previous reachable positions for each index is still too slow. Instead, we can keep a running count or window of reachable positions using prefix sums or a sliding window, which allows us to efficiently update which indices are reachable without redundant checks.
dp) of the same length as s. dp[i] will be true if index i is reachable, otherwise false. Set dp[0] to true since you always start at the first index.
i from 1 to the end:
[i - maxJump, i - minJump] is reachable (i.e., dp[j] == true for some j in that range), and s[i] is '0'.i, we maintain a running sum (call it pre) of reachable positions in the window.i - minJump enters the window, add dp[i - minJump] to pre. When i - maxJump - 1 leaves the window, subtract dp[i - maxJump - 1] from pre.pre > 0 and s[i] == '0', set dp[i] to true.dp[n-1] (where n is the length of s), indicating if the last index is reachable.
This approach is efficient because each index is processed in constant time, and we only keep track of a running sum rather than repeatedly scanning windows.
Suppose s = "011010", minJump = 2, maxJump = 3.
Let's walk through each step:
dp[0]=true.dp[1]=false.s[2]='1', so dp[2]=false.s[3]='0', so dp[3]=true.minJump=2, so check 1 to 2 back. Only 2 is in range, but dp[2]=false. So dp[4]=false.dp[3]=true, s[5]='0', so dp[5]=true.
The last index (dp[5]) is true, so the answer is true.
dp array.
The key insight is to avoid redundant work by using dynamic programming with a sliding window. Rather than checking every possible path, we only track which positions are reachable and efficiently update this information as we scan the string. This leads to a linear time solution, making it suitable for large inputs. The use of prefix sums or a running window is what makes this solution both elegant and efficient.