给你一个下标从 0 开始的二进制字符串 s 和两个整数 minJump 和 maxJump 。一开始,你在下标 0 处,且该位置的值一定为 '0' 。当同时满足如下条件时,你可以从下标 i 移动到下标 j 处:
i + minJump <= j <= min(i + maxJump, s.length - 1)且s[j] == '0'.
如果你可以到达 s 的下标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
提示:
2 <= s.length <= 10^5s[i]要么是'0',要么是'1's[0] == '0'1 <= minJump <= maxJump < s.length
分析: 如果最后一个位置本身是 '1',那么一定无法到达,可以直接返回 false。
先把字符串中所有字符为 '0' 的位置存入数组 pos 中,判断可以跳到哪些位置时,只需要在这些 '0' 位置中寻找,不需要反复扫描整个字符串。然后使用队列进行 BFS,队列中保存的是当前已经可以到达的位置。初始时,下标 0 一定是起点,所以先将 0 入队。对于队首位置 index,它下一步可以到达的范围是 [index + minJump, index + maxJump] 这个区间内,并且落点必须是 '0'。
使用变量 temp 作为指针,指向 pos 数组中当前还没有处理过的位置。又因为 pos[0] 就是起点 0,因此 temp 初值为 1。当 BFS 扩展某个位置时,就从 pos[temp] 开始向后扫描。如果 pos[temp] < left,说明这个 '0' 位置太靠前,当前点跳不到它。由于 BFS 中后续取出的下标只会越来越大,之后的 left 也只会更靠后,所以这个位置以后也不可能再被跳到,可以直接跳过。
如果 pos[temp] > right,说明这个位置已经超过了当前能跳到的范围,后面的 pos 更大,也一定超过范围,所以停止本轮扫描。
如果 left <= pos[temp] <= right,说明这个位置可以从当前下标跳到,因此将它加入队列,后续继续从这个位置向外扩展。
这个 temp 是整个 BFS 共用的,而不是每次从头开始扫描。这样可以保证每个 '0' 位置最多只会被扫描一次,从而避免超时。
最后,只需要判断最后一个位置是否被访问过,如果 flag[len - 1] 为真,说明最后一个位置可以到达;否则说明无法到达。
cpp
class Solution {
public:
bool canReach(string s, int minJump, int maxJump) {
if(s[s.length()-1]=='1')return false;
vector<int>pos;
int len=s.length(),n,temp=1;
for(int i=0;i<len;++i)
if(s[i]=='0')pos.push_back(i);
queue<int>que;que.push(0);n=pos.size();
map<int,int>flag;flag[0]=1;
while(!que.empty())
{
int index=que.front();que.pop();flag[index]=1;
int left=index+minJump,right=min(len-1,index+maxJump);
if(left>right)continue;
while(temp<n)
{
if(pos[temp]<left)temp++;
else if(pos[temp]>right)break;
else que.push(pos[temp]),flag[pos[temp]]=1,temp++;
}
}
if(flag[len-1])return true;
return false;
}
};