美团后台笔试感受附编程题思路【已更新代码】
后台岗。
听说美团主Java,然后选择题好几道是C***的,有些没见过的函数根本看不懂。。感觉选择题做的不是很好
编程贼简单,2AC,等下笔试结束我再发详细思路和代码。
第一题刚开始做的时候只有25%~33%,提示超时(Go和Java都会超时),后续改了算法才AC。。
第二题,很简单,找准限制条件就可以了,O(1)的空间复杂度可以解决。
----------分割线------------
下面的解题思路:
第二题简单,先说第二题,直接上代码,思路在注释里,语言是Go:
func max(a, b int) int { if a >= b { return a } return b } // 思路: // 条件1:每次分配的时候试卷必须够 // 条件2:最后分配的时候,不能改到自己组的卷,也就是自己的卷必须已经被前面的组改过了 // 得出目标条件: // 1. 将试卷数最多的组作为第一组,以满足条件1 // 2. 第一组的试卷总数必须少于等于其余所有组的试卷数总和,以满足条件2 func Paper() { var n int for { _, err := fmt.Scan(&n) if err == io.EOF { break } else { // 流式处理,O(1)的空间复杂度 sum := 0 // 所有试卷的总和 maxAmount := 0 // 试卷最多的组的试卷数 temp := 0 // 临时变量,用于接收输入 for i := 0; i < n; i++ { fmt.Scan(&temp) maxAmount = max(maxAmount, temp) // 记住试卷最多的组的试卷数 sum += temp // 将所有试卷加起来,得到总试卷数 } if maxAmount > sum-maxAmount { fmt.Println("No") } else { fmt.Println("Yes") } } } }
-------------分割线--------------
第一题:
题目要求 【最长的】【能够整除k的】【子序列】
所以很容易就想到穷举,我一个个试,还能找不到?
好,确定用穷举的方法的。
怎么穷举呢?前几天在牛客看到有位大佬讲解某个题的思路时候,提到的“滑动窗口”,大家还记得TCP里面的滑动窗口吗,就是一个窗口,前沿后沿可以移动,中间包着些数据的那个。
对应到这道题,我设定一个固定长度的窗口,然后在原序列上滑动,并且计算窗口中所有元素的值,然后再mod一下k,不就知道能不能整除吗?
好,然后我马上开始动手写代码,先用Go写了第一版,滑动窗口值从1开始一直到n,依次计算,然后记录最大的;跑出来的结果是25%,剩余的是超时,然后我换Java跑了一次,嗯,33%,也是超时。
后来我想了一下,既然只要最大的,为啥不从大的窗口开始查?只要找到了,就肯定没有比它更大了,就能够减少很多计算;
好,改一改就行,窗口值从大的开始找,果然跑出AC了。既然AC了,我为了省时间,就没有对第二个优化点进行优化。
后面检查的时候重复跑了几次,发现有的数据会跑出92%,看了看,还剩挺多的时间的,就把第二个优化点也做了吧。
优化点:
滑动窗口包含的序列的值,其实是不用每次都重新全量计算的。因为这题里面,滑动窗口的步进是1,所有就会有m-1个值是会重复的,换句话说,窗口在滑动的时候,其实是丢掉第一个值,然后加上最后一个值的后面一个值。
所以,只需要第一次全量计算,后面的值就可以用上一次的值加上一个值并减去一个值计算出来。
PS:还有更好的优化方法,用一个辅助数组s[n],保存0~i个数的和,那么求i~j序列的和的时候,只需要s[j]-s[i]即可!
Go代码,AC,但是没有跑很多次,不知道会不会也有数据是超时的:
// 求序列的和的辅助函数 func sumSlice(s []int) int { sum := 0 for i := 0; i < len(s); i++ { sum += s[i] } return sum } func ShiftWindow() { var n int for { _, err := fmt.Scan(&n) if err == io.EOF { break } else { seq := make([]int, n) for i := 0; i < n; i++ { fmt.Scan(&seq[i]) } var k int fmt.Scan(&k) // 从长的序列开始找,一旦找到,就必定是最长的,后面不用继续找了 found := false for windowSize := n; windowSize >= 1 && !found; windowSize-- { // 优化点:序列和不需要每次都计算,只需要第一次计算, // 后面的和,根据固定长度的滑动窗口原理,只需要减去第一项, // 并加上原序列的最后一项的下一项,就可以得到下一个序列的和。 // 可以自行脑补滑动窗口:D // 如果我不优化这里,有的数据会跑出92%,优化完就可以100%了 // 第一次,需要计算序列和 ss := sumSlice(seq[0:windowSize]) for i := 0; i <= n-windowSize; i++ { if i != 0 { // 不是第一次,就不需要从头加了,可以根据上一次的滑动窗口序列和计算出来 ss -= seq[i-1] ss += seq[i+windowSize-1] } if ss%k == 0 { // 只要找到的可以马上停止了 fmt.Println(windowSize) found = true break } } } if !found { // 没找到,打印个0吧 fmt.Println(0) } } } }
不知道各位大佬还有没有更好的思路,如果有的话希望不吝赐教!
#美团#