美团后台笔试感受附编程题思路【已更新代码】

后台岗。
听说美团主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)
			}
		}
	}
}

不知道各位大佬还有没有更好的思路,如果有的话希望不吝赐教!
#美团#
全部评论
第一题,代码。因为改了两个版本。所以有点丑。 思路:先求sum[i],表示前i项的和,然后对每个sum[i]模k,那么我们要找的就是模k后的值相同的那些数中相距最远的。 #include <iostream> #include <vector> #include <unordered_map> #include <algorithm> using namespace std; bool cmp(const pair<int,int>& a,const pair<int,int>&b){ if(a.first==b.first){ return a.second < b.second; }else{ return a.first<b.first; } } int main(){ int n; cin>>n; vector<int> a(n+1); vector<int> s(n+1,0); for(int i=1;i<=n;++i){ cin>>a[i]; s[i] = s[i-1] + a[i]; } int k; cin>>k; int res = 0; vector<pair<int,int>> mod(n+1); for(int i=1;i<=n;++i){ mod[i].first = s[i]%k; mod[i].second = i; } sort(mod.begin()+1,mod.end(),cmp); int l = 0; int tem = 0; for(int i=1;i<=n;++i){ if(mod[i].first==tem) continue; else { int tres = mod[i-1].second - mod[l].second; res = max(res,tres); tem = mod[i].first; l = i; } } int tres = mod[n].second - mod[l].second; res = max(res,tres); cout<<res<<endl; }
点赞 回复 分享
发布于 2017-08-31 21:36
Java版 import java.util.*; public class Main{ public static void main(String[] args){      Scanner in=new Scanner(System.in);         while(in.hasNext()){          int n=in.nextInt();          int k;             int[] Num=new int[n];             int sum=0;             for(int i=0;i<n;i++){              Num[i]=in.nextInt();                 sum+=Num[i];             }             k=in.nextInt();             Result(Num,n,sum,k);         }         in.close();     } public static void Result(int[] Num,int len,int sum,int k){ int count=len; while(count>0){ if(RR(sum,k)){ System.out.println(count); return; } int start=0; int end=count-1; int h=sum; while(end+1<len&&start<end){ end+=1; h=h-Num[start]+Num[end]; start++; if(RR(h,k)){ System.out.println(count); count=0; return; } } sum-=Num[count-1]; count--; }         System.out.println(0); } public static boolean RR(int n,int k){ return n%k==0; } }
点赞 回复 分享
发布于 2017-09-01 18:21
k倍的那个题目是啥啊
点赞 回复 分享
发布于 2017-09-01 09:33
已更新代码
点赞 回复 分享
发布于 2017-08-31 22:13
左了,第二道题确实可以O(1)的空间复杂度,我竟然还排序……
点赞 回复 分享
发布于 2017-08-31 21:12
第一题老师超时,改不粗来~
点赞 回复 分享
发布于 2017-08-31 21:10

相关推荐

AAA不喝拿铁:校友好,开投就完事了!要准备面试的话更建议刷codetop,hot100有些题并不是面试常考题。另外想看刷题路线的可以看我的帖子,有讲怎么刷leetcode,除此之外可以看看我根据真实面经整理得到的最全(高/中/低频)面试题,加油
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务