leetcode 第 400 场算法比赛

作者: | 更新日期:

题目都比较简单,拼手速的时候到了。

本文首发于公众号:天空的代码世界,微信号:tiankonguse

零、背景

这次比赛都不难,不过我手速慢了点,最后1题大意了没有闪,还错了一次。

A: 栈模拟。
B: 区间合并。
C: 贪心。
D: 线段树。

排名:166 代码地址: https://github.com/tiankonguse/leetcode-solutions/tree/master/contest/3/400

一、候诊室中的最少椅子数

题意:一个数字不断的加一和减一,求这个数字期间的最大值。

思路:栈模拟,这里不关心数值,所以只是使用一个数字来代替栈。

加一时数字就加一,减一时就减一,每次求 max 即可。

二、无需开会的工作日

题意:工作时长有 n 天,给一些会议列表,一个会议会跨越多个工作日,问有多少天是没有会议的。

思路:区间合并的经典题型。

需要先对区间进行排序,开始时间较早的在前面,同时开始时,结束时间的顺序无所谓。

对于这道题,有两个思路。
第一个方法是先求出所有会议天数,总天数减去会议天数,就是答案。
第二个方法是直接在合并区间时,求出答案。

这里建议使用第一个方法,不需要特殊处理边界。

代码如下,

// pre = firstDay
// [a,b]
pre = max(pre, a);
ans += max(0, b - pre + 1);
pre = max(pre, b+1);

当然,比赛的时候我使用的第二个方法,稍微浪费了一些时间。

三、删除星号以后字典序最小的字符串

题意:给一个字符串,需要删除所有的字符 * , 删除这个字符时,需要同时删除前面的任意一个最小字符。
问所有字符 * 都删除后,剩余的字符串的最小字典序是什么。

思路:贪心。

贪心1:目标是字典项最小,对于多个最小字符,显然删除最后一个最小字符最优。

贪心2:对于多个 * ,右边的删除区间包含左边的,可以证明,从左到右删除最优。

证明:
首先可以发现,每删除一个最小字符,新的字符串的字典序就会变大。

这里假设只有两个*,且只有一个最小字符在左区间,只有一个次小字符在右区间。
情况1:先删除左边,再删除右边时,最小字符和次小字符都被删除。
情况2:先删除右边时,左边的最小字符被删除。再删除左边时,左边没有最小字符了,只能删除其他字符,这就导致左边多删除一次最小字符。

分析可以发现,情况2有概率比情况1多删除一次最小字符,从而导致情况1的字典序比情况2更大。
故需要每次都先删除左边,再删除右边,即需要从左到右删除。

如何找到左边的最小值?
统计每个字符的下标升序列表。
从小到大枚举字符,找到的第一个字符就是最小字符,下标列表里最后一个就是最后一个最小字符。

四、找到按位与最接近 K 的子数组

题意:给一个数组,求一个子数组,使得子数组的且位运算后与 K 的距离最小,求最小距离。

思路:线段树。

可以发现,以某个下标为第一个元素的所有子数组,数组越长,位运算的值越小,即满足有序性。

想要找到与 K 距离最接近的下标,只需要二分找到首个大于等于 K 的子数组,枚举左右边界,即可找到最小值。

如何快速求出一个子数组的答案呢?
一个子数组对应一个区间,这个就是一个区间查询问题,显示可以使用线段树来优化。

边界问题:线段树初始化时,边界可能比实际数组元素大,此时需要初始化为 (1<<n) - 1

注意事项:这里的有序性是递减的,不要写成递增的二分查找了。

五、最后

这次比赛最后一题错了一次,原因是写二分查找时,把有序性当做递增处理了。

《完》

-EOF-

本文公众号:天空的代码世界
个人微信号:tiankonguse
公众号ID:tiankonguse-code

本文首发于公众号:天空的代码世界,微信号:tiankonguse
如果你想留言,可以在微信里面关注公众号进行留言。

关注公众号,接收最新消息

tiankonguse +
穿越