package com.heu.wsq.leetcode.zhousai.so3;
/**
* 5765. 跳跃游戏 VII
* @author wsq
* @date 2021/5/23
* 给你一个下标从 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 。
*
* 链接:https://leetcode-cn.com/problems/jump-game-vii
*/
public class Solution2 {
/**
* 动态规划,使用前缀和进行时间优化,不然会超时
* @param s
* @param minJump
* @param maxJump
* @return
*/
public boolean canReach(String s, int minJump, int maxJump) {
if(s == null || s.length() == 0){
return true;
}
int n = s.length();
char[] arr = s.toCharArray();
if(arr[n-1] != '0' || arr[0] != '0'){
return false;
}
// 定义动态规划状态数组
// dp[i]表示是否能由[i-maxJump, i-minJump]范围内跳到该索引的可能
// 当索引i对应元素为1时,dp[i] = false
// 当索引i对应元素为0时,dp[i] = dp[j] || dp[i] (j在上面的范围内,存在dp[j]为可抵达的状态,dp[i] = true)
boolean[] dp = new boolean[n];
dp[0] = true;
// 定义前缀和数组,表示dp状态数组的前缀求和,true表示为1,false表示为0
int[] pre = new int[n];
pre[0] = 1;
int i;
// 小于minJump的位置都是不可达的,pre[1:minJump]都是1+0的情况
for(i = 1; i < minJump; i++){
pre[i] = 1;
}
for(i = minJump; i < n; i++){
if(arr[i] == '0'){
int left = i - maxJump, right = i - minJump;
int total = pre[right] - (left <= 0 ? 0 : pre[left - 1]);
// total不为0时,表示left到right的范围内存在可抵达的节点
dp[i] = total != 0;
}
pre[i] = pre[i-1];
if(dp[i]){
pre[i] += 1;
}
}
return dp[n-1];
}
public static void main(String[] args) {
String s = "011010";
Solution2 solution2 = new Solution2();
boolean ans = solution2.canReach(s, 2, 3);
System.out.println(ans);
}
}