2020年3月20日阿里内推笔试题

2020年3月20日阿里内推笔试题


  因为第一批笔试的缘故加上自己家里买的摄像头还在路上,所以这一次的笔试我是没有参加的,根据网上整理出来的题目描述自己完成了这两道算法题。因为自己这样做,并没有得到验证,如果哪里有误的地方还希望大家能够积极指正。

题目描述

有一叠扑克牌,每张牌介于1和10之间
有四种出牌方法:

单出1张
出2张对子
出五张顺子,如12345
出三连对子,如112233

给10个数,表示1-10每种牌有几张,问最少要多少次能出完

题目分析

  这个题目如果需要找特定的规则去判断,显然是可以的,但是条件有点多。所以就不考虑这种方式,干脆根据题目中的几种情形分别去回溯,然后对回溯的结果去比较,选择最小的一种方式来打牌。
  能出三个对子,显然需要要求,i,i+1,i+2的数目都大于2。同理,要出顺子也要满足一定的条件,我们只需要对该条件进行分别进行判断即可。
  大致整理一下代码的核心思路,从小往大出牌,研究每一个数字可以如何出完。如果不能打连对或者连子的时候,出牌的次数是一定的,如果可以打的时候,计算打连对最少需要出的次数,打连子最少出的次数,然后打对子最少需要出的次数,然后最终取最小,就是出完当前数字的最少次数,然后依次计算每个数字出完的最小次数。
  这里我只给出核心代码,也并没有处理输入输出的情况。大家自行处理。

python代码

class Solution1:
    def leastTimes(self,nums,begin=0):
        res=sum(nums[begin:]) # 表示剩余牌的总数
        if not res:return 0
        if nums[begin]>0:
            if begin+2<10 and nums[begin]>1 and nums[begin+1]>1 and nums[begin+2]>1:
                for i in range(begin,begin+3): # 打了连对,状态改变
                    nums[i]-=2
                res=min(1+self.leastTimes(nums,begin),res)
                for i in range(begin,begin+3): # 回溯,恢复状态
                    nums[i]+=2

            if begin+4<10 and nums[begin+1] and nums[begin+2] and nums[begin+3] and nums[begin+4]:
                for i in range(begin, begin + 5): # 打了连子,状态改变
                    nums[i] -= 1
                res=min(1+self.leastTimes(nums,begin),res)
                for i in range(begin, begin + 5): # 回溯,恢复状态
                    nums[i] += 1
            if nums[begin]>1:
                nums[begin] -= 2 # 出对子,因为出对子之后,可能当前的牌还有剩余,所以继续从当前牌型去搜索。
                return min(1+self.leastTimes(nums,begin),res)

            else : # 只有一张,只能考虑单张
                return min(res,1+self.leastTimes(nums,begin+1))
        else: # 表示没有当前牌型,直接打下一张牌
            return self.leastTimes(nums,begin+1)

  这个代码就是一个普通的回溯法,需要注意的是虽然知道这个递归有很多的重复计算,但是因为牌只有10种类型,没有必要优化了。这个题目,是无法优化成动态规划的(我没有想到)。
  我们可以在递归的时候,使用中间数目为0的牌型把数组分段,然后最终的结果就是对多段分别递归再求和即可,根据上面的代码很好改。不过多赘述。

题目描述

首先定义上升字符串,对于任意的 0 < i < l e n ( s ) 0<i<len(s) ,s[i]≥s[i−1],比如aaa,abc是,acbd不是
给n个上升字符串,选择任意个拼起来,问能拼出来的最长上升字符串长度

  这个题目很类似于一个经典的题目,课表安排的问题,课表安排的问题可以使用动态规划,也可以使用贪心。但是这个题目和课表安排的题目有一点不同的地方就是课表只需要安排的课程数目多即可,而该问题需要总的字符串长度较长。这个区别决定了这个问题不能使用贪心处理。
  但是课程安排问题的动态规划方法还是可以使用的,所以这个题目就是动态规划。
  我们定义一个上升字符串为 S i j S_{ij} ,表示该字符串的首字符是 i ,尾字符是 j,则 d p j = m a x ( d p j 1 , d p i + S i j . s i z e ( ) ) i j dp_j=max(dp_{j-1},dp_i+S_{ij}.size())\quad i\le j 。解释一下这个公式,表示的是以字符j结尾的上升字符串最长的长度是以字符j-1结尾的上升字符串的最长长度,和以字符i结尾加上字符串从 S i j S_{ij} 的长度的最大值。需要注意这里的i 不是一个值,而是所有可能的取值。
  我们要处理的问题是根据结尾字符j 来处理的,所以我们需要对每一个字符串根据结尾字符排一个序。为了防止’hhhh’这种情况提前出现,因为这种情况提前出现了,就会过早使用 d p h dp_{*h} 的和 S h h S_{hh} 长度更新 d p h dp_{*h} 的结果,我们就需要把 S h h S_{hh} 放在 S h S_{*h} 之后处理。
  也就是说,我们根据最后一个字符排序,如果最后一个字符相同,我们则需要把第一个字符越大的越往后边放。而且排序的过程中,因为最后一个字符只有26种情况,这里我们可以考虑使用基数排序或者桶排序,排序我这里直接使用的是内建的排序,如果大家需要优化可以采用其他的。具体优化细节不谈。

python代码

def longestAscString(strs:List[str])-> int:
    strs=sorted(strs,key=lambda x:(x[-1],x[0]))
    dp=[0]*26
    res=0
    last=0
    for string in strs:
        begin=ord(string[0])-ord('a')
        end=ord(string[-1])-ord('a')
        for i in range(last,end): # 没有这些字符结尾的最长长度,使用前面的最长长度更新
            dp[i+1]=dp[last]
        dp[end]=max(dp[end],dp[begin]+len(string))
        res=max(res,dp[end])
        last=end

    return res

a=[
    "bcdefhijk",
    "bcd",
    "aaa",
    "eeeefghhh",
    "zzzz",
]
b=['abc',
'hpq',
'qrt',
'jklmnopqr',
'abcjklmnopqr',]

c=['abcd',
'deft',
'efghmnt',
'defghjkl',
'abcddefghjkl',]
print(longestAscString(c))

  我这里以字符 j j 结尾的最大长度的上升字符串长度使用数组来存储,为了方便映射,使用ACill码值来映射,所以是ord(i)-ord('a')
  可以看到空间复杂是 O ( n ) O(n) 的,算法的时间复杂度瓶颈在排序这里,如果使用 O ( n ) O(n) 级别的排序可以将算法的复杂度降到 O ( n ) O(n) 。使用普通的排序,则时间复杂度是 O ( n l g n ) O(nlgn)

发布了36 篇原创文章 · 获赞 4 · 访问量 47万+

猜你喜欢

转载自blog.csdn.net/m0_38065572/article/details/105019514
今日推荐