leetcode 第 325 场算法比赛

作者: | 更新日期:

带病比赛,翻车了。

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

零、背景

不知道是不是确诊后变笨了,这次比赛题目我做的很慢,比赛期间没做完。

一、到目标字符串的最短距离

题意:给一个字符串数组和起始位置,可以双向遍历,问最近的与查询字符串相等的距离。

思路1:枚举所有相等的字符串,然后计算出两个方向的距离,取最小值即可。

思路2:枚举两个方向,查找即可。

二、每种字符至少取 K 个

题意:给一个字符串,问取前缀与取后缀后,是否可以使得每个字符都至少有 k 个,求前后缀长度最小和。

思路:预处理后缀和,枚举前缀,二分后缀。

PS:比赛的时候,我一直想复用 STL 的 lower_bound,编写比较函数纠结了好久。
最终还是手写二分通过了。

注意事项:如果前缀已经满足答案,就不需要去二分后缀了。

三、礼盒的最大甜蜜度

题意:给一个数组,问怎样选k个数字,才能使得 k 个数字的最小绝对值最大。

思路:最小值的最大值,二分答案即可。

细节:先对数字排序,检查答案是否满足时,根据第一个数字计算出下个最小的数字,然后再使用二分查找第一个大于等于的位置,循环找 k 个即可。

小技巧:第二个二分可以使用lower_bound

四、好分区的数目

题意:给一个数组,问是否可以分为两个子序列,使得两个子序列和都大于等于 k,求满足要求的划分数。

思路1:总划分数是 2^n个,先求不满足的,相减就是满足的。

如果整个数组和小于 2*k,那显然没有答案。
其他情况,不满足的肯定是一个子序列小于k,一个不小于 k。
k 数据范围只有 1000, 可以通过动态规划计算出子序列和为 [0,k]的子序列个数。

状态:dp[i][j] 前 i 个元素子序列和为 j 的子序列个数。
i+1 个元素根据选择与不选择,更新状态即可。
复杂度:O(n^2)

小技巧:可以通过逆序计算,把二维数组转化为一维数组。

思路2:分类动态规划。

前 i 个元素的状态定义:

状态1:dp1[i][] 第一个子序列和不大于 k 时的子序列个数。
状态2:dp2[i][] 第一个子序列和大于等于k但第二个不大于k时的子序列个数。
状态3:dp3[i] 两个子序列和都大于等于k时的个数。

转移方程:

状态1可以转向状态1或者状态2或状态3。
状态2可以转向状态2或者状态3。
状态3可以转向状态3。

对于状态3,由于以满足答案,所以新来的元素不管选择哪个子序列,都满足答案,所以答案翻倍。

对于状态2,如果选择子序列1,更新自己的状态。
如果选择子序列2,子序列2的和大于等于k,则更新状态3,否则更新自己。

对于状态1最复杂。
如果选择子序列2,则当前状态不变。
如果选择子序列1,子序列和不大于k时,更新自身状态。
子序列和大于等于k时,需要根据前缀和判断子序列2的和,也大于等于k了,直接进入状态3,否则进入状态2。

复杂度:O(n^2)

对比下,第一个方法只需要一个简单的普通动态规划即可解决,而第二个方法则维护了三个状态,状态内部可以转化,转态间也可以转化,变得相当复杂。

四、最后

这次比赛第二个为了复用 STL 折腾了好久,以后二分如果需要写比较函数,那就自己实现二分查找吧,太浪费时间了。

加油,算法人。

《完》

-EOF-

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

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

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

tiankonguse +
穿越