美团后台笔试感受附编程题思路【已更新代码】
后台岗。
听说美团主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)
}
}
}
} 不知道各位大佬还有没有更好的思路,如果有的话希望不吝赐教!
#美团#