题目
给你一个下标从 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^5
s[i] 要么是 ‘0’ ,要么是 ‘1’
s[0] == ‘0’
1 <= minJump <= maxJump < s.length
思路
普通的动态规划很好想,但是肯定会超时。我们设表示能不能到达
位置,
因为对于每个i我们都遍历了个前面的位置,看有没有一个可达的位置,所以超时。一个优化的思路是,我们将
由
型改为
型,使用
的好处是只要
内有一个
值为
就好,而这个条件可以用前缀和在
#card=math&code=O%281%29&id=YeaB4)时间内求解。因此除了
数组在维护一个前缀和数组
,判断
位置能否到达时,只要判断
是否大于
即可。
有一些细节要注意,一是和
都可能小于
,二是下标
不同于后面的位置,不需要满足
区间和大于
,三是
数组可以省略,只需要最后到了
位置判断一下即可。
代码
dp+前缀和
class Solution {
public boolean canReach(String s, int minJump, int maxJump) {
int n = s.length();
int[] pre = new int[n + 1];
for (int i = 0; i < n; i++) {
// pre[i + 1]最少为pre[i]
pre[i + 1] = pre[i];
// 只有遇到0才可能到达
if (s.charAt(i) == '0') {
int right = i - minJump;
int left = Math.max(i - maxJump, 0);
// 区间右边界最小为0,同时区间和大于0,就表示当前位置可达;或者i == 0表示0下标可达
if (right >= 0 && pre[right + 1] - pre[left] > 0 || i == 0) {
if (i == n - 1) {
return true;
}
// 原来使用了dp数组这里有dp[i] = 1,同时更新pre[i + 1]
pre[i + 1]++;
}
}
}
return false;
}
}
有序集合
这道题是当时一场周赛的题目,当时没想到前缀和,用的有序集合,现在回看还觉得自己当时很厉害(其实是现在变菜了…
有序集合题目都是有规律可循的,再比如题,两个题目都是关于两个变量在两个不等式约束下的问题,使用 TreeSet 可以空间换时间,以
#card=math&code=log%28n%29&id=U52SF) 而非
#card=math&code=O%28n%29&id=NeH6p)时间查找出满足题意的值。
class Solution {
public static boolean canReach(String s, int minJump, int maxJump) {
int n = s.length();
char[] arr = s.toCharArray();
// set存储了可以到达的点的下标
TreeSet<Integer> sortedSet = new TreeSet<>();
sortedSet.add(0);
boolean ans = false;
// 判断i位置是否可达
for (int i = 0; i < n; i++) {
if (arr[i] == '1') continue;
// j+min<=i<=j+max (注意i j和题目中相反 这里j是上一个位置)
// 上式右边不等式可变形为j>=i-max 因此先找最小的j
// ------一个小问题------
// ------为什么要找最小的而不是最大的j呢------
// ------因为如果找到的小的j都不满足左边不等式 j+min<=i 那么大的一定更不满足了 所以找小的就好了------
// ------小问题解答结束------
// 如果找到j 再判断左边不等式是否成立 如果也成立 则说明i可达 并将i加入set中 另外当i==n-1时 就将答案置为true
// 如果没找到j 说明当前i不可达 看下一个i
Integer ceil = sortedSet.ceiling(i - maxJump);
if (ceil != null && ceil + minJump <= i) {
if (i == n - 1) ans = true;
sortedSet.add(i);
}
}
return ans;
}
}