Leetcode 第 42 场双周赛题解(Python)

Leetcode 第 42 场双周赛题解

周赛日期:2020/12/26

题目1:1700. 无法吃午餐的学生数量  难度: 简单

学校的自助午餐提供圆形和方形的三明治,分别用数字 0 和 1 表示。所有学生站在一个队列里,每个学生要么喜欢圆形的要么喜欢方形的。
餐厅里三明治的数量与学生的数量相同。所有三明治都放在一个 栈 里,每一轮:

如果队列最前面的学生 喜欢 栈顶的三明治,那么会 拿走它 并离开队列。
否则,这名学生会 放弃这个三明治 并回到队列的尾部。
这个过程会一直持续到队列里所有学生都不喜欢栈顶的三明治为止。

给你两个整数数组 students 和 sandwiches ,其中 sandwiches[i] 是栈里面第 i​​​​​​ 个三明治的类型(i = 0 是栈的顶部), students[j] 是初始队列里第 j​​​​​​ 名学生对三明治的喜好(j = 0 是队列的最开始位置)。请你返回无法吃午餐的学生数量。

 

示例 1:

输入:students = [1,1,0,0], sandwiches = [0,1,0,1]
输出:0 
解释:
- 最前面的学生放弃最顶上的三明治,并回到队列的末尾,学生队列变为 students = [1,0,0,1]。
- 最前面的学生放弃最顶上的三明治,并回到队列的末尾,学生队列变为 students = [0,0,1,1]。
- 最前面的学生拿走最顶上的三明治,剩余学生队列为 students = [0,1,1],三明治栈为 sandwiches = [1,0,1]。
- 最前面的学生放弃最顶上的三明治,并回到队列的末尾,学生队列变为 students = [1,1,0]。
- 最前面的学生拿走最顶上的三明治,剩余学生队列为 students = [1,0],三明治栈为 sandwiches = [0,1]。
- 最前面的学生放弃最顶上的三明治,并回到队列的末尾,学生队列变为 students = [0,1]。
- 最前面的学生拿走最顶上的三明治,剩余学生队列为 students = [1],三明治栈为 sandwiches = [1]。
- 最前面的学生拿走最顶上的三明治,剩余学生队列为 students = [],三明治栈为 sandwiches = []。
所以所有学生都有三明治吃。
示例 2:

输入:students = [1,1,1,0,0,1], sandwiches = [1,0,0,0,1,1]
输出:3
 

提示:

1 <= students.length, sandwiches.length <= 100
students.length == sandwiches.length
sandwiches[i] 要么是 0 ,要么是 1 。
students[i] 要么是 0 ,要么是 1 。

题解:计数

本题最简单的做法就是指针+模拟,但其实有更简单的做法,时间复杂度为o(n)。

我们先想想什么情况下,会有同学无法吃饭,

举个例子,当students=[0,0,0] sandwitches = [1, 0, 0]的时候sandwitches就无法再出栈,处于了卡死状态,则有三个学生没法吃饭,

反之,如果此时students中有一个1,那么还可以继续,所以只需要一开始统计学生0和1的数量,然后不断出栈sandwiches,直到“卡死”或者所有三明治出栈。

“卡死”状态可以具体为:已经没有同学喜欢当前栈顶种类的三明治了,即count[sandwiched[0]] == 0。最后剩余的了students的数量就是吃不到三明治的人数。

class Solution:
    def countStudents(self, students: List[int], sandwiches: List[int]) -> int:
        count = [0, 0]
        for s in students:
            count[s] += 1
        for sandwich in sandwiches:
            if count[sandwich] == 0: break # "卡死"
            count[sandwich] -= 1
        return count[1] + count[0]

题目2:1701. 平均等待时间  难度: 中等

有一个餐厅,只有一位厨师。你有一个顾客数组 customers ,其中 customers[i] = [arrivali, timei] :

arrivali 是第 i 位顾客到达的时间,到达时间按 非递减 顺序排列。
timei 是给第 i 位顾客做菜需要的时间。
当一位顾客到达时,他将他的订单给厨师,厨师一旦空闲的时候就开始做这位顾客的菜。每位顾客会一直等待到厨师完成他的订单。厨师同时只能做一个人的订单。厨师会严格按照 订单给他的顺序 做菜。

请你返回所有顾客需要等待的 平均 时间。与标准答案误差在 10-5 范围以内,都视为正确结果。

 

示例 1:

输入:customers = [[1,2],[2,5],[4,3]]
输出:5.00000
解释:
1) 第一位顾客在时刻 1 到达,厨师拿到他的订单并在时刻 1 立马开始做菜,并在时刻 3 完成,第一位顾客等待时间为 3 - 1 = 2 。
2) 第二位顾客在时刻 2 到达,厨师在时刻 3 开始为他做菜,并在时刻 8 完成,第二位顾客等待时间为 8 - 2 = 6 。
3) 第三位顾客在时刻 4 到达,厨师在时刻 8 开始为他做菜,并在时刻 11 完成,第三位顾客等待时间为 11 - 4 = 7 。
平均等待时间为 (2 + 6 + 7) / 3 = 5 。
示例 2:

输入:customers = [[5,2],[5,4],[10,3],[20,1]]
输出:3.25000
解释:
1) 第一位顾客在时刻 5 到达,厨师拿到他的订单并在时刻 5 立马开始做菜,并在时刻 7 完成,第一位顾客等待时间为 7 - 5 = 2 。
2) 第二位顾客在时刻 5 到达,厨师在时刻 7 开始为他做菜,并在时刻 11 完成,第二位顾客等待时间为 11 - 5 = 6 。
3) 第三位顾客在时刻 10 到达,厨师在时刻 11 开始为他做菜,并在时刻 14 完成,第三位顾客等待时间为 14 - 10 = 4 。
4) 第四位顾客在时刻 20 到达,厨师拿到他的订单并在时刻 20 立马开始做菜,并在时刻 21 完成,第四位顾客等待时间为 21 - 20 = 1 。
平均等待时间为 (2 + 6 + 4 + 1) / 4 = 3.25 。
 

提示:

1 <= customers.length <= 105
1 <= arrivali, timei <= 104
arrivali <= arrivali+1

题解:模拟

处理的顺序题目已经告知,当有个顾客来,当前厨师要是空闲,就直接开始做他的订单,如果厨师非空闲,就只能等待他现在和之前等待的人都做好了,再开始他的订单。

所以用cur_time表示厨师的最近的空闲时间,

从头遍历顾客,对于当前顾客,

  • 如果厨师的最近空闲时间在他到店之前(cur_time <= arrival),就可以直接做,那么res += time,而厨师的最近时间更新为cur_time += time。
  • 如果厨师非空闲(cur_time > arrival),那么该用户必须等到厨师最近的空闲时间再开始做,  res += cur_time - arrival + time, cur_time += time。
class Solution:
    def averageWaitingTime(self, customers: List[List[int]]) -> float:
        if not customers: return 0.0
        cur_time = 0
        res = 0
        for arrival, time in customers:
            if cur_time <= arrival:
                cur_time = arrival + time
                res += time
            else:
                cur_time += time
                res += cur_time - arrival  
        return res / len(customers)

 

题目3:1702. 修改后的最大二进制字符串  难度: 中等

给你一个二进制字符串 binary ,它仅有 0 或者 1 组成。你可以使用下面的操作任意次对它进行修改:

操作 1 :如果二进制串包含子字符串 "00" ,你可以用 "10" 将其替换。
比方说, "00010" -> "10010"
操作 2 :如果二进制串包含子字符串 "10" ,你可以用 "01" 将其替换。
比方说, "00010" -> "00001"
请你返回执行上述操作任意次以后能得到的 最大二进制字符串 。如果二进制字符串 x 对应的十进制数字大于二进制字符串 y 对应的十进制数字,那么我们称二进制字符串 x 大于二进制字符串 y 。

 

示例 1:

输入:binary = "000110"
输出:"111011"
解释:一个可行的转换为:
"000110" -> "000101" 
"000101" -> "100101" 
"100101" -> "110101" 
"110101" -> "110011" 
"110011" -> "111011"
示例 2:

输入:binary = "01"
输出:"01"
解释:"01" 没办法进行任何转换。
 

提示:

1 <= binary.length <= 105
binary 仅包含 '0' 和 '1' 。

题解:思维题

为了得到最大值,我们一定要让最高位尽量变为1。
我们可以发现一个规律:如果存在多个0,最后结果一定只存在一个0,如果存在2个0以上,那么第一个0后面的每一个0都可以让第一个0后移1位。

Leetcode 第 42 场双周赛题解(Python)_第1张图片
所以答案为第一个0的位置加上总共0的个数-1位为0,其余位都为1。


class Solution:
    def maximumBinaryString(self, binary: str) -> str:
        cnt = 0
        first = -1
        for i in range(len(binary)):
            if binary[i] == '0':
                if first == -1: first = i
                else: cnt += 1
        if cnt:
            res = list('1'*len(binary))
            res[first+cnt] = '0'
            return "".join(res)
        else: return binary

题目4:1703. 得到连续 K 个 1 的最少相邻交换次数  难度: 困难

给你一个整数数组 nums 和一个整数 k 。 nums 仅包含 0 和 1 。每一次移动,你可以选择 相邻 两个数字并将它们交换。

请你返回使 nums 中包含 k 个 连续 1 的 最少 交换次数。

 

示例 1:

输入:nums = [1,0,0,1,0,1], k = 2
输出:1
解释:在第一次操作时,nums 可以变成 [1,0,0,0,1,1] 得到连续两个 1 。
示例 2:

输入:nums = [1,0,0,0,0,0,1,1], k = 3
输出:5
解释:通过 5 次操作,最左边的 1 可以移到右边直到 nums 变为 [0,0,0,0,0,1,1,1] 。
示例 3:

输入:nums = [1,1,0,1], k = 2
输出:0
解释:nums 已经有连续 2 个 1 了。
 

提示:

1 <= nums.length <= 105
nums[i] 要么是 0 ,要么是 1 。
1 <= k <= sum(nums)

题解:贪心+前缀和+滑动窗口

记录1的位置,放在数组a中,假设连续的第一个位置为x,那么结果为:
|a0-x| + |a1-(x+1)| + ... + |ak-(x+k)|
我们把a中的元素ai映射为ai-i,那么根据贪心的结论,我们只需要计算把v中的连续k个数移动到中位数位置上的元素的步数和,取最小值。
计算步数和可以用前缀和优化。

然后计算代价:

mid左边:(a[mid] - a[l])+ (a[mid] - a[l+1]) + ...... + (a[mid] - a[mid-1]) = a[mid] * (mid - l) + s[mid-1] - s[l-1]

mid右边:(a[mid+1] - a[mid])+ (a[mid+2] - a[mid]) + ...... + (a[r] - a[mid]) = s[r] - s[mid] - a[mid] * (r - mid)

总代价:mid左边 + mid右边

class Solution:
    def minMoves(self, nums: List[int], k: int) -> int:
        a = []
        j = 0
        for i in range(len(nums)):
            if nums[i] == 1:
                a.append(i-j)
                j += 1
        s = [0]*(len(a)+1)
        a = [0] + a
        for i in range(1, len(a)):
            s[i] = s[i-1]+a[i] # 前缀和

        res = float('inf')
        for l in range(1, len(a)-k+1):
            r = l + k - 1
            mid = l + r >> 1
            x = a[mid]
            left = x * (mid-l) - (s[mid-1] - s[l-1])
            right = s[r] - s[mid] - x * (r-mid)
            res = min(res, left + right)
        return res

 

你可能感兴趣的:(Leetcode周赛,算法题,每日算法题,python,leetcode,队列,字符串)