leetcode 第 376 场算法比赛
作者:
| 更新日期:这次题目出的都挺好的。
本文首发于公众号:天空的代码世界,微信号:tiankonguse
零、背景
周末早上我八点之前就出门去练车了,所以就没参加比赛。
下午有《星光大赏》保障活动,所以题目做的比较晚,题解现在才发出来。
A: 离散数学题,循环节替换,找重复数字。
B: 排序
C: 枚举+滑动游标 或者 数学公式直接计算答案。
D: 二分+数学公式。
比赛代码:
https://github.com/tiankonguse/leetcode-solutions/tree/master/contest
一、找出缺失和重复的数字
题意:给一个n*n
矩阵,填充1~n^2
的数字,有一个数字重复了,问哪个数字重复了,以及哪个数字缺少了。
思路:数据范围很小,方法很多。
1)转化为一维数组排序。
时间复杂度:O(n log(n))
空间复杂度:O(n)
2)hash 储存与判断。
时间复杂度:O(n)
空间复杂度:O(n)
3)循环节置换
时间复杂度:O(n)
空间复杂度:O(1)
,无新增空间。
置换原理:
不妨假设题目是一维数组,排序后下标与值就会相等。
对于输入数组,如果位置 i 的值是 v,我们交换 nums[i]
与 nums[v]
,则位置 v 的值就是 v 了。
这样不断地交换,除了缺少的数字对应的位置,其他所有位置上都是自己下标对应的值。
二、划分数组并满足最大差限制
题意:给一个数组,问是否可以将数组划分为3个数字一组,且组内的数字之差不大于k。
思路:排序,3 个一组,判断是否合法。
三、使数组成为等数数组的最小代价
题意:给一个数组,每次可以将一个数字加一或者减一。
问是否可以将数组的所有数字修改为相同的回文数字,如果可以,求最小修改次数。
思路1:枚举回文数,计算修改代价,求最小值。
题目要求回文数不大于10^9
,则满足要求的回文数个数为 1.1 * 10^5
个。
我们枚举 1 ~ 10^4 - 1
, 反转可以组成回文串,可以中间是否重合有两种情况,故回文数为 2 * 10^4
个。
对于 10^4 ~ 10^5 - 1
, 组成的回文串只能取中间重合的部分,故个数有 9 * 10^4
个。
两个合起来就是 1.1 * 10^5
个。
例如对于数字 123 可以得到下面两个回文数。
中间不重合,得到 6 位回文数 123321
。
中间重合,得到 5 位回文数 12321
。
计算修改代价:二分找到位置,左边的都需要加,右边的都需要减,通过前缀和优化可以得到答案。
复杂度:O(srqt(MaxV) * log(n))
思路2:枚举回文数,游标快速找到位置。
按顺序枚举回文数,这样就不需要二分查找位置了,直接利用上个回文数的位置快速转移到最新的位置。
计算答案时也没必要使用前缀和,直接维护左右边的个数和前后缀和,游标转移时计算新的答案即可。
复杂度:O(srqt(MaxV))
思路3:直接计算答案。
从小到大枚举答案的时候,可以发现答案只与左右边数字的个数有关系。
ans = min(ans, preAns + leftNum - rightNum);
如果右边数字个数多了,向右边移动时,答案会更优。
如果两边数字相等,移动不影响答案。
如果右边数字个数较少,则向右边移动是,答案会更差。
所以可以推理出答案就是中位数。
直接计算中位数,然后求中位数附近的回文数,求最优答案即可。
复杂度:O(1)
怎么求中位数附近的回文数呢?
与枚举回文数的逻辑相反,根据中位数的奇偶性,得到枚举的数字,然后再枚举这个数字附近区间的数字即可。
四、执行操作使频率分数最大
题意:给一个数组,每次可以将一个数字加一或者减一,问最多修改 k 次,可以得到的最大频率是多少。
最大频率:数字出现的最多次数。
思路1:二分最大频率,判断是否可以在 k 次修改内得到这个频率。
假设区间 [l, r]
的数字被修改为相同的数字,如何求最小修改次数呢?
是不是和第三题一模一样?
答案就是修改为中位数时修改代价最小,通过前缀和可以快速计算出答案。
那怎么找到区间 [l,r]
呢,枚举所有区间即可。
复杂度:O(n log(n))
思路2:枚举中位数目标数字,二分区间半径,判断 k 次是否可以修改为相同值,从而得到的最大频率。
复杂度:O(n log(n))
PS:由于半径分为左半径和右半径,处理起来稍微复杂点。
五、最后
这次比赛最后两题还是比较有意思的,很经典的题目。
互动问题:你推导出中位数就是答案了吗?
《完》
-EOF-
本文公众号:天空的代码世界
个人微信号:tiankonguse
公众号ID:tiankonguse-code
本文首发于公众号:天空的代码世界,微信号:tiankonguse
如果你想留言,可以在微信里面关注公众号进行留言。