leetcode 第 374 场算法比赛
作者:
| 更新日期:比赛有点难度,去练车了,没参加比赛。
本文首发于公众号:天空的代码世界,微信号:tiankonguse
零、背景
周末早上我七点之前就出门去练车了,所以就没参加比赛。
中午趁着天热回来休息的时间,做了一下比赛题,写一下题解。
A: 循环签到题。
B: 组合题型,循环一遍即可。
C: 统计枚举题,循环枚举判断是不是答案。
D: 排列组合题。
总结:难度其实不大,但是最后一题竟然卡常数时间,vector 换成数组就过了。
比赛代码:
https://github.com/tiankonguse/leetcode-solutions/tree/master/contest
一、找出峰值
题意:给一个数组,求所有山峰。
山峰定义:严格大于左右相邻的数字。
思路:循环判断即可。
二、需要添加的硬币的最小数量
题意:给一些硬币,问至少需要几个硬币,才能组成[1,sum]
的所有金钱。
思路:组合题。
如果某些硬币可以组成[1,x]
,来一个硬币y
,则可以组成[1,x] + [y,x+y]
。
如果y<=x+1
,两个区间可以合并,即组成区间[1,x+y]
。
如果y>x+1
,从区间 [1,x+1]
中新增一个硬币才能组合出 x+1
,显然选择x+1
组成的新区间是最大的,选择其他的都有浪费。
另外,可以发现上面这个规则必须从小到大来算才行,否则答案也不是最优的。
故先对硬币排序,然后按上面规则计算即可。
注意事项:给的硬币都用完时,可能还不满足答案,需要继续循环添加新硬币,直到满足答案。
三、统计完全子字符串
题意:给一个字符串,问有多少个子串是完全字符串。
完全字符串:出现的字符个数都是 k,且相邻字符的差值不大于2。
思路:统计枚举题。
枚举每个起始位置,枚举出现的字符个数,即可得到子串对应的区间,判断子串出现的字符是否满足答案。
复杂度:O(26^2 n)
剪枝1: 判断子串区间是否合法。
剪枝2: 判断区间内是否都满足相邻字符差值的条件(差值算法)。
剪枝3: 判断某个字符个数是否都大于 k。
判断是否满足答案:枚举所有字符在区间内出现的次数,看是否都是 k 个。
判断复杂度:O(26)
差值算法:
预处理:出所有不满足条件2的相邻对(储存左下标)。
预处理复杂度: O(n)
判断:二分查找子串内第一个不满足条件2的下标。
判断复杂度:log(n)
计算字符在区间内出现的次数
预处理:预处理所有字符在所有前缀内出现的次数。
预处理复杂度: O(26 n)
区间次数:前缀求差值
判断复杂度:O(1)
综合复杂度:O(26^2 n)
这样就可以通过了。
优化:枚举起始位置后,可以快速推导出可能满足答案的子串。
第一步:假设字符出现 a 个,子串长度为 a*k
,并判断是否触发剪枝。
第二步:未触发剪枝,判断长度为 a*k
的子串内出现的不同字符个数。
如果个数小于 k,触发剪枝。
如果大于 k,比如为 b 则回到第一步,令 a=b
计算。
如果等于 k,判断是否是答案,之后回到第一步,令 a=a+1
计算。
子串内出现的不同字符个数:
前缀差判断,复杂度O(26)
线段树判断,复杂度`O(log(n))
四、统计感冒序列的数目
题意:n个人站在一排,有若干人感冒了。
感冒的人会传染给相邻的人,但是每一秒只有一个人会被传染。
问这些人被传染的顺序的组合个数。
思路:排列组合题。
假设一个连续区间有 k 个人。
中间的一个连续区间,每一次可以从左边传染,也可以从右边传染,一个区间内的组合数是2^(k-1)
中方案数。
队首和队尾连续未生病的人只能从中间来传染,方案数为 1
个。
假设共有 N 个人没生病。
第一个连续区间有 k1 人,可能分配到 N 个位置,组合数为 C(N,k1)
。
第二个连续区间有 k2 人,可能分配到 N-k1
个位置,组合数为 C(n-k1, k2)
,
其他的同理。
故总方案数如下:
C(N, k1) // 对首
* C(N-k1, k2) * 2^(k2-1)
* C(N-k1-k2, k3) * 2^(k3-1)
* ...
* C(kn, kn) // 队尾,值为1,忽略
由于C(n,k)
比较大,可以使用逆元模板来快速计算。
五、最后
这次比赛总结如下:
第一题:签到题。
第二题,简单构造题,需要一番逻辑推理与证明。
第三题:两重枚举题,出的不好,复杂度有O(26^2 n)
。
第四题:排列组合题,属于排列组合里较为基础的题型。
后两题你都是怎么做的呢?
《完》
-EOF-
本文公众号:天空的代码世界
个人微信号:tiankonguse
公众号ID:tiankonguse-code
本文首发于公众号:天空的代码世界,微信号:tiankonguse
如果你想留言,可以在微信里面关注公众号进行留言。