nineloong'sblog
首页归档照片墙音乐杂谈友链关于
封面
点击查看全图

跳跃游戏 VII

2026/5/18
# BFS
# 滑动窗口

题目描述

LeetCode 1871 — 跳跃游戏 VII (Medium)

给定一个下标从 0 开始的二进制字符串 s 和两个整数 minJump、maxJump。最初,你位于下标 0,且 s[0] == '0'。

当你位于下标 i 时,可以跳到下标 j,需同时满足:

  • i + minJump <= j <= min(i + maxJump, s.length - 1)
  • s[j] == '0'

请判断能否到达下标 s.length - 1。能则返回 true,否则返回 false。

示例 1:

  • 输入:s = "011010", minJump = 2, maxJump = 3
  • 输出:true
  • 解释:从 0 跳到 3,再从 3 跳到 5(终点)。

示例 2:

  • 输入:s = "01101110", minJump = 2, maxJump = 3
  • 输出:false
  • 解释:从 0 能跳到 3;从 3 向右只能到 5 和 6,但 s[5] = '1' 且 s[6] = '1',无法继续。到不了终点 7。

约束:

  • 2 <= s.length <= 10^5
  • s[i] 为 '0' 或 '1'
  • s[0] == '0'
  • 1 <= minJump <= maxJump < s.length

核心思路

朴素 BFS 的问题

题意很自然地导向 BFS——从下标 0 出发,每次将 [i+minJump, i+maxJump] 范围内的合法 '0' 位置入队,直到到达终点或队列为空。

但直接实现的代价很高。考虑最坏情况:

s = "00000...000"(全 0),minJump = 1, maxJump = n-1

  • 下标 0 出队,扫描范围 [1, n-1],全部 n-1 个位置入队 → O(n)
  • 下标 1 出队,扫描范围 [2, n-1],全部 n-2 个位置——但都已访问,白扫 → O(n)
  • 下标 2 出队,扫描范围 [3, n-1],n-3 个位置又白扫 → O(n)

每个节点出队都 O(n),总时间 O(n²)。n = 10^5 时,约 50 亿次操作,必然超时。

根本问题:每次出队,我们都在重复扫描已经被前序节点覆盖过的区间。

滑动窗口优化

观察一个关键性质:跳转方向只有向前(j >= i + minJump),BFS 处理的节点下标天然递增。这意味着一件事——

下标 i 出队时,[0, i+maxJump] 的左侧区间已经被前面的节点"扫过"了。我们唯一需要关心的,是从「上次扫到的位置 + 1」到 i+maxJump 这一段新区间。

这正是滑动窗口的思路:维护一个指针 furthest,表示已探索到的最远下标。对每个出队节点 i:

  • 扫描起点 = max(i + minJump, furthest + 1)(跳过已探索区域)
  • 扫描终点 = min(i + maxJump, n - 1)

furthest 单向递增,数组中每个下标最多被检查一次。总扫描次数从 O(n²) 降为 O(n)。


解法:BFS + 滑动窗口

bool canReach(string s, int minJump, int maxJump) {
    int n = s.length();
    // 终点如果是 '1',不可能到达
    if (s[n - 1] == '1') return false;

    queue<int> q;
    q.push(0);

    // furthest:已扫描到的最远下标(滑动窗口右边界)
    int furthest = 0;

    while (!q.empty()) {
        int i = q.front();
        q.pop();

        // 滑动窗口:只扫描区间内的未探索部分
        int left  = max(i + minJump, furthest + 1);
        int right = min(i + maxJump, n - 1);

        for (int j = left; j <= right; j++) {
            if (s[j] == '0') {
                if (j == n - 1) return true;
                q.push(j);
            }
        }

        // 窗口右边界向前滑动
        furthest = max(furthest, right);
    }

    return false;
}

复杂度: 时间 O(n),空间 O(n)。每个下标最多入队一次、被 for 循环检查一次。

相关题目

  • LeetCode 55 — 跳跃游戏
  • LeetCode 45 — 跳跃游戏 II
  • LeetCode 1306 — 跳跃游戏 III
  • LeetCode 1345 — 跳跃游戏 IV
avatar

nineloong

一隅之地,深耕自我

RECOMMENDED

二分查找:从经典模板到应用题

2026/5/23

每日一题:检查数组是否经排序和轮转得到

2026/5/23

每日一题:统计特殊字母的数量 II

2026/5/27

Table of Contents