[LeetCode 1871] 跳跃游戏 7(Ⅶ)

题面:

数据范围:

2 ≤ s . l e n g t h ≤ 1 0 5 2 \le s.length \le 10^5 2≤s.length≤105
s i si si 要么是 ′ 0 ′ '0' ′0′ ,要么是 ′ 1 ′ '1' ′1′
s 0 = = 0 s0 == 0 s0==0
1 ≤ m i n J u m p ≤ m a x J u m p < s . l e n g t h 1 \le minJump \le maxJump < s.length 1≤minJump≤maxJump<s.length

思路 & Code

重点: 当 s i = = 0 si==0 si==0 时,才能继续跳,也才能被跳到。

So,自然会想到存储所有能够跳到的 0 0 0 点,而当前 0 0 0 点必然是从存储的 0 0 0 点跳过来的。容易想到可以暴力状态转移

用一数组 d p dp dp 存储下标状态:

  1. d p i n d e x = = 0 dpindex==0 dpindex==0,说明 i n d e x index index 位置没有被跳到
  2. d p i n d e x = = 1 dpindex==1 dpindex==1, i n d e x index index 可以被跳到

那么,对于当前点 i n d e x index index 都得判断 m i n ( i − m a x J u m p ) , 0 ) , i − m i n J u m p min(i-maxJump), 0), i-minJump min(i−maxJump),0),i−minJump 区间内的所有可跳点,这样太暴力了, O ( n k ) O(nk) O(nk)。必须考虑优化。

差分维护

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)

维护个前缀和,每次到 s i = = ′ 0 ′ si=='0' si==′0′(可跳点)时,判断区间 m i n ( i − m a x J u m p ) , 0 ) , i − m i n J u m p min(i-maxJump), 0), i-minJump min(i−maxJump),0),i−minJump是否存在可跳点 0 0 0,即 p r e i − m i n J u m p − p r e i − m a x J u m p − 1 prei-minJump - prei-maxJump-1 prei−minJump−prei−maxJump−1是否大于零。(当然了,区间得注意下)

Code:

cpp 复制代码
bool canReach(string s, int minJump, int maxJump) {
    int n = s.size();
    vector<int> dp(n, 0), pre(n, 0);
    dp[0] = 1;
    for(int i = 0; i < minJump; ++i) pre[i] = 1;
    for(int i = minJump; i < n; ++i) {
        int l = i - maxJump, r = i - minJump;
        // 差分判断可跳区间内是否存在 0 点
        if(s[i] == '0' && pre[r] - (l >= 1 ? pre[l - 1] : 0) > 0) dp[i]=1;
        pre[i] = pre[i-1] + dp[i];
    }
    return dp[n-1];
    /* 
    当然了,不用dp数组维护也可以
    写成:
    for(int i = minJump; i < n; ++i) {
		int l = i - maxJump, r = i - minJump;
		if(s[i] == '0' && pre[r] - (l >= 1 ? pre[l - 1] : 0) > 0) {
			if(i == n-1) return true;
			pre[i] = pre[i-1]+1;
		} else pre[i] = pre[i-1];
	}
	return false;
    */
}

当然了,由于可跳区间其实就是一个滑动窗口,直接拿个临时变量存储可跳点个数就行,但得存个状态哈

Code:

cpp 复制代码
bool canReach(string s, int minJump, int maxJump) {
    int n = s.size(), cnt = 1;
    vector<bool> dp(n, false);
    dp[0] = true;
    for(int i = minJump; i < n; ++ i) {
        if(s[i] == '0' && cnt) dp[i] = true;
        // 可跳区间要右移啦(其实就是动态维护滑动窗口的思想啦)
        // 如果左端点是可跳的,cnt--;如果要加入的点(右端点)是可跳的,cnt++
        if(i - maxJump >= 0 && dp[i - maxJump]) --cnt;
        if(i - minJump + 1 < n && dp[i - minJump + 1]) ++ cnt;
    }
    return dp[n-1];
}

动态维护滑动窗口(可跳区间)

上面都提到滑动窗口了,那直接拿个队列去维护滑动窗口也很简单啦。时间复杂度和空间复杂度都是 O ( n ) O(n) O(n)。

Code:

cpp 复制代码
bool canReach(string s, int minJump, int maxJump) {
    int n = s.size();
    if(s[n-1] == '1') return false;
    queue<int> q;
    q.push(0);
    for(int i = minJump; i < n; ++i) {
        if(s[i] == '1') continue;
        while(!q.empty() && q.front() < i - maxJump) q.pop();
        if(!q.empty() && q.front() <= i - minJump) {
            if(i == n-1) return true;
            q.push(i);
        }
    }
    return false;
}
相关推荐
LB21121 分钟前
力扣102 198 70 55
数据结构·算法·leetcode
鹿角片ljp26 分钟前
LeetCode 42:接雨水|前后最大值DP
算法·leetcode·动态规划
北域码匠9 小时前
高通滤波算法深度解析(High-Pass Filter)
stm32·算法·c#·数字信号处理·嵌入式开发·滤波算法·高通滤波
指掀涛澜天下惊9 小时前
强化学习进阶篇八 策略梯度算法
深度学习·学习·算法·强化学习
乌萨奇也要立志学C++9 小时前
【洛谷】kmp算法
开发语言·算法
禹凕9 小时前
Dijkstra算法详解与应用
python·算法
YaraMemo10 小时前
元启发式算法框架
人工智能·算法·5g·信息与通信·启发式算法·信号处理
weixin_3077791311 小时前
一维无粘 Burgers 方程的激波形成问题:MacCormack 格式求解
c++·算法·matlab
HugoStudio_SWAN11 小时前
洛谷 B4500 / B4449 / B3843 凯撒密码、密码强度与密码合规——加密与安全的三道门
c++·学习·程序人生·算法·安全