题目描述:
给你一个下标从 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
。
数据范围:
$2\le s.len \le 10^5$
题解:
不像之前的每次可以至多跳 $num$ 个,而且不是每个位置都可以起跳。可以想象到,最终得到的是一些区间,可以针对遍历到的下标 $i$ 使用区间加 $[i + minJump, i + maxJump]$ ,得到下一段覆盖的区间。对于一个位置 $i$ 判断是否能起跳,需要看是否被区间覆盖了。
线段树:
很简单,直接维护区间加,判断某一个位置是否被区间覆盖了即可。
动态规划:
$dp[i] = dp[i - maxJump] | \cdots|dp[i - minJump]$ ,但是复杂度比较高。可以把 $|$ 运算转化为求区间和运算。每次针对 $i$ 求区间 $[i - maxJump, i - minJump]$ 区间和,如果区间和大于 $0$ 则说明可以跳到。然后需要边遍历边维护区间和, $preSum[i] = preSum[i - 1] + (sum \neq 0)$ 。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| auto optimize_cpp_stdio = []() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout.tie(nullptr); return 0; }(); class Solution { public: const static int maxn = 1e5 + 10; const static int maxm = 1e5 + 10; const static long long mod = 1e9 + 7; const long long INF_LL = 0x3f3f3f3f3f3f3f3f; const int INF = 0x3f3f3f3f; bool canReach(string s, int minJump, int maxJump) { if (s.back() != '0') return false; int n = s.length(); vector<int> dp(n); vector<int> preSum(n + 1); function<int(int)> getPreSum = [&](int index) { if (index < 0) return 0; return preSum[index]; }; preSum[1] = 1; for (int i = 2; i <= n; ++i) { if (s[i - 1] == '0') { int sum = getPreSum(i - minJump) - getPreSum(i - maxJump - 1); preSum[i] = preSum[i - 1] + (sum != 0); if (i == n && sum != 0) return true; } else { preSum[i] = preSum[i - 1]; } } return false; } };
|