LeetCode 242周赛
Posted Zephyr丶J
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 242周赛相关的知识,希望对你有一定的参考价值。
242周赛
今日参加第一次参加周赛,昨天看了一下周赛的奖励,积分挺多的,而且自认为做了也有不短时间题了,最起码能过两道吧,结果就过了一道,虽说在宿舍做电脑用着确实不太习惯,虽说第一次参加有点不太熟悉流程,虽说第二道题我感觉我是做的对的,哈哈,不管咋样,还是菜,搞的我心态有点崩
5763. 哪种连续子字符串更长
题目描述
给你一个二进制字符串 s 。如果字符串中由 1 组成的 最长 连续子字符串 严格长于 由 0 组成的 最长 连续子字符串,返回 true ;否则,返回 false 。
例如,s = "110100010" 中,由 1 组成的最长连续子字符串的长度是 2 ,由 0 组成的最长连续子字符串的长度是 3 。
注意,如果字符串中不存在 0 ,此时认为由 0 组成的最长连续子字符串的长度是 0 。字符串中不存在 1 的情况也适用此规则。
示例 1:
输入:s = "1101"
输出:true
解释:
由 1 组成的最长连续子字符串的长度是 2:"1101"
由 0 组成的最长连续子字符串的长度是 1:"1101"
由 1 组成的子字符串更长,故返回 true 。
示例 2:
输入:s = "111000"
输出:false
解释:
由 1 组成的最长连续子字符串的长度是 3:"111000"
由 0 组成的最长连续子字符串的长度是 3:"111000"
由 1 组成的子字符串不比由 0 组成的子字符串长,故返回 false 。
示例 3:
输入:s = "110100010"
输出:false
解释:
由 1 组成的最长连续子字符串的长度是 2:"110100010"
由 0 组成的最长连续子字符串的长度是 3:"110100010"
由 1 组成的子字符串不比由 0 组成的子字符串长,故返回 false 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longer-contiguous-segments-of-ones-than-zeros
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
唯一通过的一道题,看了一下,用了13分钟,当时感觉也就用了10来分钟,最起码第二道应该也能做出来
写的有点长,不过当时也没管那么多,直接交了过了
class Solution {
public boolean checkZeroOnes(String s) {
//最长连续子串
int l = s.length();
char c = s.charAt(0);
int max0 = 0;
int max1 = 0;
int count = 1;
for(int i = 1; i < l; i++){
char curr = s.charAt(i);
if(curr == c){
count++;
}else{
if(c == '1'){
max1 = Math.max(max1, count);
count = 1;
}else{
max0 = Math.max(max0, count);
count = 1;
}
}
c = curr;
}
if(c == '1'){
max1 = Math.max(max1, count);
}else{
max0 = Math.max(max0, count);
}
return max1 > max0;
}
}
5764. 准时到达的列车最小时速
题目描述
给你一个浮点数 hour ,表示你到达办公室可用的总通勤时间。要到达办公室,你必须按给定次序乘坐 n 趟列车。另给你一个长度为 n 的整数数组 dist ,其中 dist[i] 表示第 i 趟列车的行驶距离(单位是千米)。
每趟列车均只能在整点发车,所以你可能需要在两趟列车之间等待一段时间。
例如,第 1 趟列车需要 1.5 小时,那你必须再等待 0.5 小时,搭乘在第 2 小时发车的第 2 趟列车。
返回能满足你准时到达办公室所要求全部列车的 最小正整数 时速(单位:千米每小时),如果无法准时到达,则返回 -1 。
生成的测试用例保证答案不超过 10^7 ,且 hour 的 小数点后最多存在两位数字 。
示例 1:
输入:dist = [1,3,2], hour = 6
输出:1
解释:速度为 1 时:
- 第 1 趟列车运行需要 1/1 = 1 小时。
- 由于是在整数时间到达,可以立即换乘在第 1 小时发车的列车。第 2 趟列车运行需要 3/1 = 3 小时。
- 由于是在整数时间到达,可以立即换乘在第 4 小时发车的列车。第 3 趟列车运行需要 2/1 = 2 小时。
- 你将会恰好在第 6 小时到达。
示例 2:
输入:dist = [1,3,2], hour = 2.7
输出:3
解释:速度为 3 时:
- 第 1 趟列车运行需要 1/3 = 0.33333 小时。
- 由于不是在整数时间到达,故需要等待至第 1 小时才能搭乘列车。第 2 趟列车运行需要 3/3 = 1 小时。
- 由于是在整数时间到达,可以立即换乘在第 2 小时发车的列车。第 3 趟列车运行需要 2/3 = 0.66667 小时。
- 你将会在第 2.66667 小时到达。
示例 3:
输入:dist = [1,3,2], hour = 1.9
输出:-1
解释:不可能准时到达,因为第 3 趟列车最早是在第 2 小时发车。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-speed-to-arrive-on-time
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
当时看到这个题,瞬间就想到了二分,虽然预感到这个double会有问题,但是我感觉测试几次应该能过,心里很开心
开始我想着是把开始二分时候的左右边界先确定一下,然后就想着最慢的速度应该是 距离/时间, 最快应该是一个小时经过一个距离最长的地点;然后二分,确定当前速度能不能到达,不行就把速度变大,反之减小
然后败倒在一个[1,1,100000],2.01(几个0我记不清了)测试用例上,给的答案是更多的0的一个数,然后我就想到了,最后一站这个小数点后面的数会起作用,而且右边界也不能这么给。然后索性就把左边界直接写成1,右边界直接写成最大值了,然后就发现最初的几个示例也过不了了,输出都是1,然后我就懵了,咋回事???
可能当时有点紧张吧,我现在一看,给最大值以后,因为我写的是left+right,当时也是图省事,没写(right - left) / 2 + left…直接越界了…真想捶死自己…
下面是我当时写的代码
class Solution {
public int minSpeedOnTime(int[] dist, double hour) {
//第一辆车发车时间是0, 第二辆车最早发车时间是1,后面最早发车时间以此类推;
//因此最慢的速度是 和 / hour
//最快的速度是max
//然后二分求最小
//int max = 0;
//int sum = 0;
int l = dist.length;
//for(int dis : dist){
// max = Math.max(max, dis);
// sum += dis;
//}
if(hour < (l - 1) * 1.0)
return -1;
int left = 1;
int right = Integer.MAX_VALUE;
while(left < right){
int mid = (left + right) >> 1;
if(judge(dist, mid, hour)){
right = mid;
}else{
left = mid + 1;
}
}
return left;
}
public boolean judge(int[] dist, int mid, double hour){
for(int dis : dist){
if(dis * 1.0 / (mid * 1.0) > (double)(dis / mid))
hour -= (double)(dis / mid) + 1;
else{
hour -= (double)(dis / mid);
}
}
return hour >= 0.0;
}
}
改了那个右边界以后,然后把判断方法中,最后一步的判断重新写了一下,就过了…
不过向上取整看了一下还是写的繁琐了
向上取整可以用(dist[i] + mid - 1) / mid
class Solution {
public int minSpeedOnTime(int[] dist, double hour) {
//第一辆车发车时间是0, 第二辆车最早发车时间是1,后面最早发车时间以此类推;
//因此最慢的速度是 和 / hour
//最快的速度是max
//然后二分求最小
int l = dist.length;
if(hour < (l - 1) * 1.0)
return -1;
int left = 1;
int right = 10000000;
while(left < right){
int mid = (left + right) >> 1;
if(judge(dist, mid, hour)){
right = mid;
}else{
left = mid + 1;
}
}
return left;
}
public boolean judge(int[] dist, int mid, double hour){
for(int i = 0; i < dist.length - 1; i++){
int dis = dist[i];
if(dis * 1.0 / (mid * 1.0) > (double)(dis / mid))
hour -= (double)(dis / mid) + 1;
else{
hour -= (double)(dis / mid);
}
}
return hour - dist[dist.length - 1] * 1.0 / (mid * 1.0) >= 0.0;
}
}
5765. 跳跃游戏 VII
题目描述
给你一个下标从 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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jump-game-vii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
由于在前面那个题卡了很久,做这道题没多长时间了,
但是当时一看,这不就是动态规划吗,然后就写了下面代码
class Solution {
public boolean canReach(String s, int minJump, int maxJump) {
int l = s.length();
boolean[] dp = new boolean[l];
dp[0] = true;
for(int i = 1; i < l; i++){
if(s.charAt(i) == '1'){
dp[i] = false;
continue;
}
int left = i - maxJump;
int right = i - minJump;
for(int j = left; j <= right; j++){
if(j < 0)
continue;
if(dp[j]){
dp[i] = true;
break;
}
}
}
return dp[l - 1];
}
}
超时了,有点意外,不过要是不超时,应该也不算个中等题了,最后想了一会没想出来怎么优化,就去看了一眼最后一题,到点了,吃饭
主要优化点,应该是中间线性查找那部分,这里前缀和用的很巧妙
是dp数组的前缀和
可以将为true的点,dp[i]值赋为1,这样,如果一个点能从left到right范围内跳过来,那么说明这个范围内肯定有dp[left] + dp[left + 1]… + dp[right] != 0,而这个式子可以用求前缀和的方式以O(1)的时间复杂度得到!
具体实现过程中,在计算dp数组的同时计算前缀和数组
class Solution {
public boolean canReach(String s, int minJump, int maxJump) {
//使用前缀和优化
//如果能转移,那么left到right之间肯定dp[i]的和是大于1的
int l = s.length();
int[] dp = new int[l];
int[] pre = new int[l];
pre[0] = 1;
dp[0] = 1; //表示该位置可达
for(int i = 1; i < l; i++){
int left = i - maxJump;
int right = i - minJump;
if(s.charAt(i) == '0'){
//如果右边界小于0,则无法转移
if(right < 0){
}
//右边界不小于0,左边界小于0,那么可以从0到right转移
else if(left <= 0){
dp[i] = pre[right] != 0 ? 1 : 0;
}else{
dp[i] = (pre[right] - pre[left - 1]) != 0 ? 1 : 0;
}
}
pre[i] = pre[i - 1] + dp[i];
}
return dp[l - 1] == 1;
}
}
看到另一个巧妙的思路:
之前一直都想的是当前位置从前面位置转移而来,而这个思路是根据当前位置,确定后面多少个位置可以到达;如果不加以优化,会导致重复判断很多位置,会超时。因此需要优化,对于已经判断过的位置,设置一个变量来标记上一次循环中,到达的最右边位置,下次判断根据这个位置来选择左边界left。
class Solution {
public boolean canReach(String s, int minJump, int maxJump) {
//另一个思路,如果能到达当前位置,则标记后面能达到的位置,并对重复部分进行优化
int l = s.length();
int[] dp = new int[l];
dp[0] = 1; //表示该位置可达
int limit = 0; //上一轮达到的最远下标
for(int i = 0; i < l; i++){
//如果该位置可达
if(dp[i] == 1){
int right = Math.min(i + maxJump, l - 1);
int left = Math.max(limit, i + minJump);
for(int j = left; j <= right; j++){
if(s.charAt(j) == '0')
dp[j] = 1;
}
limit = right;
}
if(limit == l - 1)
break;
}
return dp[l - 1] == 1;
}
}
5766. 石子游戏 VIII
题目描述
Alice 和 Bob 玩一个游戏,两人轮流操作, Alice 先手 。
总共有 n 个石子排成一行。轮到某个玩家的回合时,如果石子的数目 大于 1 ,他将执行以下操作:
选择一个整数 x > 1 ,并且 移除 最左边的 x 个石子。
将 移除 的石子价值之 和 累加到该玩家的分数中。
将一个 新的石子 放在最左边,且新石子的值为被移除石子值之和。
当只剩下 一个 石子时,游戏结束。
Alice 和 Bob 的 分数之差 为 (Alice 的分数 - Bob 的分数) 。 Alice 的目标是 最大化 分数差,Bob 的目标是 最小化 分数差。
给你一个长度为 n 的整数数组 stones ,其中 stones[i] 是 从左边起 第 i 个石子的价值。请你返回在双方都采用 最优 策略的情况下,Alice 和 Bob 的 分数之差 。
示例 1:
输入:stones = [-1,2,-3,4,-5]
输出:5
解释:
- Alice 移除最左边的 4 个石子,得分增加 (-1) + 2 + (-3) + 4 = 2 ,并且将一个价值为 2 的石子放在最左边。stones = [2,-5] 。
- Bob 移除最左边的 2 个石子,得分增加 2 + (-5) = -3 ,并且将一个价值为 -3 的石子放在最左边。stones = [-3] 。
两者分数之差为 2 - (-3) = 5 。
示例 2:
输入:stones = [7,-6,5,10,5,-2,-6]
输出:13
解释:
- Alice 移除所有石子,得分增加 7 + (-6) + 5 + 10 + 5 + (-2) + (-6) = 13 ,并且将一个价值为 13 的石子放在最左边。stones = [13] 。
两者分数之差为 13 - 0 = 13 。
示例 3:
输入:stones = [-10,-12]
输出:-22
解释:
- Alice 只有一种操作,就是移除所有石子。得分增加 (-10) + (-12) = -22 ,并且将一个价值为 -22 的石子放在最左边。stones = [-22] 。
两者分数之差为 (-22) - 0 = -22 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/stone-game-viii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
博弈的这种题,也做了几道了,一看到还是懵逼
关键点1:因为每次移除过后,还要把得到的分数放入原数组的最左边,所以数组的总和是不会变化的,而且每次取都是取这个数组的一个前缀和,因此需要预先处理前缀和
关键点2:题目中所说的,Alice目标是最大化分差,Bob目标是最小化分差,其实两个人的目标都是使自己的分数最大的同时,另一个人所能获得的分数最小,也就是分差最大,所以两个人的策略是相同的
关键点3:从模拟开始,一步步优化
其他就不写了,自己也整不太明白,这种题慢慢来吧
参考题解:https://leetcode-cn.com/problems/stone-game-viii/solution/bao-li-on-2-on-dpyou-hua-xue-dao-liao-by-6peh/
总结一下这种博弈题,两个人都时最优解,那么两个人的策略一般来说是一样的,然后就对这个过程先试着模拟,一般来说都时递归;然后再对递归进行优化
另一种情况,就是有数学规律,在什么条件下,一定赢或者一定输,这种题一般返回值是boolean类型,需要数学推导
class Solution {
public int stoneGameVIII(int[] stones) {
//看完没想太明白到底该怎么办,
//下一个人的分数是上一个人的分数,加上当前选择的分数,因此总分不会发生变化
//因为是从左边开始,因此每次一个人选择的是都是这个数组的前缀和
//所以A的目标是使他自己的前缀和最大的同时B的前缀和最小
//设dp[i]表示当Alice可以选择的下标u在[i,n)范围内时,Alice与Bob 分数的最大差值
int l = stones.length;
int[] pre = new int[l];
pre[0] = stones[0];
for(int i = 1; i < l; i++){
pre[i] = pre[i - 1] + stones[i];
}
int[] dp = new int[l];
dp[l - 1] = pre[l - 1];
for(int i = l - 2; i >= 1; i--){
dp[i] = Math.max(dp[i + 1], pre[i] - dp[i + 1]);
}
return dp[1];
}
}
以后要是周末有空就参加周赛,感觉挺好的。这次马马虎虎算做了两道,下次目标三道,冲!!!!!
以上是关于LeetCode 242周赛的主要内容,如果未能解决你的问题,请参考以下文章