2020年4月10号春招实习拼多多笔试题解

昨天刚做的拼多多笔试题,说一下题解思路,第三题开始没什么时间写了,所以没提交,不知道能不能全AC。第三、四题,可以仅当做思路,因为是做完才想明白的。

第一题:给两个数组l1,l2,一个提前量offset,一个数字n。l1,l2是商品列表,offset是已经展示过的商品数量,n是要求展示的商品数量,输出要展示的商品在l1,l2里的区间,左闭右开。

例如l1=4,l2=4,offset=2,n=4。l1已经有两个展示过了,所以L1商品区间为(2,4);L2为(0:2)。如果有列表被跳过(也就是offset>l1的长度),区间两个数字用该列表长度代替,若果有列表不需要商品展示(offset+n

考察数组,很简单,输出要求的数组区间即可,需要弄明白的就是几个边界条件而已。

第二题:给一个n,n是商品的数量,再给一个大小为n的数组,数组元素值表示商品的价格,买三个,其中价格最低的免费。输出购买所有商品需要的金额。例如 6 7 8 5,输出20

贪心策略,先对价格按降序排序,递推,每遇到第三个,总金额不动,其他都加商品价格就可以了。

第三题:给出n,k,再给出一个大小为n的一串数字(每个数字【0,9】)。目的是将这个数组变为至少有K个数字相同的新数组,并且变化过程中,差值要最小,如果有好几种方案,输出字典序最小的。例如n=6,k=5,数字串:787585,答案是777577,差值为4.答案有777577和777775两种,第一种字典序最小。

解决方法:先将数字串里数字出现的次数记录下来,先创建一个大小为10的数组,arr=[0]*10。787585,下标为7,8,5各两个。

第一步,选中要成为k次的那个数字target,从大到小进行循环,对于787585分别是8,7,5,此时我们需要变动k-arr[target]次。为什么要从大到小呢,因为答案要字典序最小,那么相同差值的情况下,我们从大到小循环,自然循环到后面的答案数组字典序要比前面的小(因为我们选的target更小)。

第二步,再用两个指针(pre_i=i-1,end_i=i+1)分别指向前面和后面的元素,我们需要改动数组,让target出现K次,那么自然将target数字附件的数字变为target,差值会最小。又因为输出答案字典序要小,那显然优先变动值大的右边。

k=k-arr[target]     k为需要变动的次数

if end_i<10 and arr[end_i]:
    if arr[end_i]<=k:
        k=k-arr[end_i]
        arr[target]+=arr[end_i]
        arr[end_i]=0
        cur_min+=arr[end_i]*(end_i-target)
    else:
        arr[target]+=k
        cur_min+=k*(end_i-target)
        k=0
        arr[end_i]-=k
if pre_i>0 and arr[pre_i]:
    跟end_i差不多的处理
end_i+=1
pre_i-=1    #两个指针一起挪动一格
if cur_min>min:#如果发现当前差值已经大于全局差值,不需要再继续,说明当前这个target是错的
    break
if k==0 and cur_min<=min:
    min=cur_min
    res_arr=arr

对于pre_i同理,先判断end_i接着判断pre_i,保证每次循环,end_i+=1和pre_i-=1同时,因为始终要找最近的元素。循环直到k=0,如果cur_min<=min,存储新的差值以及arr数组。因为我们所有的循环都从大到小,所以相同差值情况下,后更新的那个arr数组永远是字典序更小的。另外

(如果这题是787686,那应该先将8变为7:777686,而不是先变6:777787,这点我写的时候没考虑到,所以循环的时候从小到大,先变了6,也导致没AC,后面交卷脑子清醒下来才发现,实在可惜。)

第三步:通过前面两步我们已经得到结果差值和数组,但我们得到的数组arr并不是最终的数字串而是一格存储了数字串中数字出现次数的数组。例如我们最后得到的结果是arr[7]=5,arr[5]=1,arr[else]=0的数组而不是777577。我们需要利用arr将原来的数字787585转换成777577。

我们遍历这个数字串,遇到一个数字n,如果arr[n]=0,将这个数字转成target,如果arr[n]>0,将arr[n]-=1。

arr[8]=0,arr[5]=1那么遇到的8都会被转为7,遇到的第一个5让arr[5]减一变为0,后面的5转为7,转换后的数字串就是777577。

总结下第三题的题解:

为了获得arr数组,我们需要遍历一遍原字符串O(n)

1:选定target,target只会从0到9,循环为10次,与原数字串的长度无关,O(1)的时间复杂度

2:对于每个target,经过第二步的处理,pre_i+end_i的循环次数显然也是10次。

3:用得到的arr数组将原数字串转化为新的数字串O(n)

最终时间复杂度位O(n),本题难点因在于相同差值的情况下,如果确保答案字典序最小,当然我们可以把所有答案都存储下来,输出其中最小的那个,但显然空间复杂度太高,而且我们得到的是arr数组,还需要对每个答案进行转化。所以我们利用target的选取从大到小以及数字转变也优先转变大的数字来确保后更新的arr字典序最小。

这题我只ac了15%,原因是我target的选取与数字的转变(pre_i在end_i前面)都是从小到大,所以字典序反而最大,最简单的例子就是787686,从小到大为787777(因为先转pre_i,也就是6,转完才去转end_i)从大到小则是777677。

笔试的时候一定要冷静,冷静,冷静。笔试题最不会特别难,如果冷静下来考虑清楚边界条件和一些约束条件,还是挺容易AC,我就是太慌了,导致没思考清楚字典序和循环之间的对应关系。只要循环都改为从大到小,应该可以通过。

第四题:给一个数组n以及一个k,最多可以移除k的元素,求给出数组最大的连续子数组长度。例如[1,1,1,2,1,3,3,4],k=2.答案是4

典型的DP题,这题leetcode上有简单版,也就是k固定1或者2。美团笔试的第二题好像就是k=1的情况。

当时写完第三题已经只有4分钟了,来不及写第四题,不过第一反应就知道是个DP题。之前遇到的都是k是固定的,所以这题一开始没什么思路。后面突然想到k固定的时候只是dp的数组大小也固定了,那么k其实影响的只是dp的数组大小而已。

题解:dp的行数显然是数组长度n,那么列数呢?哈哈,列数其实就是k+1。dp的核心思想不就是保存之前的结果吗,那么我们可以移除k个元素,显然会有k+1(0也是一条)条不同的结果。那么列数为k+1是很自然的事情。下面上一个代码

arr=[1,2,2,1,1,2,2,4]
n=len(arr)
k=2
res=0
dp=[1]*n
for i in range(n):
    dp[i]=[1]*(k+1)
for i in range(1,n):
    for t in range(k+1):
        if i-t-1<0:
            break
        if arr[i]==arr[i-t-1]:
            for q in range(t,k+1):
                dp[i][q]=dp[i-t-1][q-t]+1
                res=max(dp[i][q],res)
            break
#print(dp)
print(res)

这题如果想通了DP的核心想法其实就不难。我们把每次循环都当做一次决策,当前决策是根据之前的决策与当前的元素来进行。例如原题目明显可以通过递推来解决,递推显然有前后关系,DP可以保留前一步的决策,并通过状态转移方程来生成当前的决策,如果k=0,那么非常简单,就是算数组的最长连续数组。DP数组就是1*n,每次决策只需要转移一种状态,而如果k=1呢,那么每次决策显然有两种状态,一种k=0,也就是不需要转移元素的情况,k=1,转移一种元素的情况,DP数组就是2*n,也很简单,无非就是两条状态转移方程。所以dp的数组就是(k+1)*n,我们需要有k条状态转移方程。

那么只需要明白状态转移方程怎么写就可以解决问题了。

以[1,2,2,1,1,2,2,4],k=2为例子。dp的结果为:

[[1, 1, 1], [1, 1, 1], [2, 2, 2], [1, 1, 2], [2, 2, 3], [1, 1, 3], [2, 2, 4], [1, 1, 1]]。

k=2,那么i-1,i-2,i-3分别对应k=0,k=1,k=2的情况,判断arr[i]和上面三个哪个相等,来找到i元素决策所需要的上一次决策信息。

例如下标为5的时候,arr[5]=2,arr[4]=1,arr[3]=1,arr[2]=2,arr[5]==arr[2],那么dp[5]的状态就应该由dp[2]来更新,我们需要更新三种状态,分别是k=0,1,2的情况,5和2之间相隔两个元素,对应k=2的状态,所以dp[5][2]=dp[2][2-2]+1。也就是dp[2]k=0对应dp[5]k=2。而dp[5]k=0,k=1(0-2,1-2)都不在范围内,所以不更新,默认为1。

 

OK。四道题的题解都讲完了,3,4题我都没AC,第三题很可惜,代码写出来,但没考虑字典序的问题,写完因为没什么时间,脑子也有点乱,所以就没继续改,而是看第四题去了,想起来还是有点后悔。

第四题,我当时没写,只记住了题目,后面想了下写了个解法,不确定是否能满足所有用例,有看出问题的欢迎评论探讨。

 

 

 

你可能感兴趣的:(面试经历,算法)