LeetCode 每日一题笔记
0. 前言
- 日期:2026.05.25
- 题目:1871. 跳跃游戏 VII
- 难度:中等
- 标签:动态规划、前缀和、差分数组
1. 题目理解
问题描述 :
给定一个下标从 0 开始的二进制字符串 s 和两个整数 minJump 和 maxJump。
- 从下标
0出发 - 在下标
i处,可以跳跃到[i + minJump, i + maxJump]范围内 - 只能落在值为
'0'的下标上
判断能否到达最后一个下标。
示例:
输入:s = "011010", minJump = 2, maxJump = 3
输出:true
2. 解题思路
核心观察
- 每个位置
i能否到达,取决于区间[i-maxJump, i-minJump]内是否有可达位置。 - 用前缀和 快速查询区间内可达位置数量,用差分数组快速标记可达区间。
算法步骤
- 初始化可达标记与前缀和/差分数组。
- 遍历每个位置,判断是否能从前面的位置跳过来。
- 用前缀和快速查询区间和,用差分数组批量更新可达区间。
- 最后一位是否可达即为答案。
3. 代码实现
java
package lc1871;
import java.util.Arrays;
public class Solution {
public boolean canReach(String s, int minJump, int maxJump) {
int n = s.length();
boolean[] dp = new boolean[n];
dp[0] = true;
int[] pre = new int[n + 1];
pre[1] = 1;
for (int i = 1; i < n; i++) {
int L = Math.max(0, i - maxJump);
int R = i - minJump;
if (R < 0) {
dp[i] = false;
} else {
if (pre[R + 1] - pre[L] > 0 && s.charAt(i) == '0') {
dp[i] = true;
} else {
dp[i] = false;
}
}
pre[i + 1] = pre[i] + (dp[i] ? 1 : 0);
}
return dp[n - 1];
}
}
4. 代码优化说明
java
class Solution {
public boolean canReach(String s, int minJump, int maxJump) {
// 转为字符数组,提高访问效率
char ch[]=s.toCharArray();
int n=ch.length;
// 差分数组,用于区间更新可达标记
int diff[]=new int[n];
// 当前区间内可达位置的数量
int sum=0;
// 终点是1,直接不可能到达
if(ch[n-1]=='1'){
return false;
}
// 初始化差分数组:0位置可达,区间[0,0] +1
diff[0]=1;
diff[1]=-1;
for(int i=0;i<n;i++){
// 计算当前位置的可达状态(前缀和)
sum+=diff[i];
// 当前位置不可达,直接跳过
if(sum<=0){
continue;
}
// 当前位置是0,可以跳跃
if(ch[i]=='0'){
// 计算能跳到的左右边界
int start=i+minJump;
int end=Math.min(i+maxJump,n-1);
// 区间无效,跳过
if(start>end){
continue;
}
// 直接跳到终点,返回true
if(end==n-1){
return true;
}
// 差分数组区间更新:[start,end] +1
diff[start]+=1;
diff[end+1]-=1;
}
}
// 遍历结束未到达终点
return false;
}
}
5. 复杂度分析
- 时间复杂度 :O(n)O(n)O(n)
一次遍历 + 差分数组 O(1)O(1)O(1) 区间更新。 - 空间复杂度 :O(n)O(n)O(n)
差分数组空间开销。
6. 总结
- 核心:差分数组 + 前缀和 优化跳跃区间的可达性更新。
- 优势:避免 O(n2)O(n^2)O(n2) 暴力遍历,效率大幅提升。
- 本质:用区间标记代替逐个标记,是跳跃游戏的经典优化思路。