python新手刷题之:最佳人选 | 豆包MarsCode AI刷题

100 阅读4分钟

问题描述:

某特种部队采用了一套性格密码机制来筛选执行特定任务的最佳士兵,该机制的规则如下:

  1. 每个人的性格可以通过M个维度来描述,每个维度分为 A,B,C,D,E 五种类型。
  2. 同一维度内,字母距离越近,性格类型差异越小,匹配程度越高。比如,A和B的差异为1,A和D的差异为3。
  3. 其中 AE、BD、CE、BE 为不相容性格类型,差异值设为无穷大(无法匹配)。
  4. 如果某一维度存在不相容性格类型,则表示两个士兵性格完全不匹配。
  5. 对于符合匹配条件的士兵,差异值总和越小表示匹配程度越高。

现在,有一个重要的机密任务,要求找到最匹配该任务所需性格密码的士兵。你需要编写一个算法,帮助部队找到符合条件的最佳人选。

  • m表示性格密码的维度。
  • n表示备选特种兵的数量。
  • target是代表任务的性格密码.
  • array是一个包含n个元素的列表,每个元素为m位的性格密码。

测试样例:

输入:m = 6, n = 3, target = "ABCDEA", array = ["AAAAAA", "BBBBBB", "ABDDEB"] 输出:'ABDDEB'
输入:m = 5, n = 15, target = "ACABB", array = ["EAACE", "AABCC", "DCBCD", "ECCAE", "BBACD", "DCCAB", "BBDBD", "BCAAD", "EABAC", "CBCDA", "DBBAE", "ABDBB", "EDDAD", "DBBAC", "BDACC"] 输出:'ABDDEB'
输入:m = 4, n = 4, target = "AEBC", array = ["ACDC", "BBDC", "EBCB", "BBBB"] 输出:'None'

解题过程:

这道题的意思是,给定一个目标序列,需要在现有序列组合中找到与目标序列最相近的组合,序列之间按位比较。有一点题目中没有说明的是,最终输出的序列可以有多个,详见样例2。按照题目中的规则,可以看出五种类型之间的差异实际是它们对应ASCII码的差异,但同时又给出了一些特殊“屏蔽词”。那么,解题思路就很简单了,计算每个现有序列和目标序列的差值,然后在所有差值中找到最小值,输出对应序列即可。具体实现如下:

ban = ['AE', 'EA', 'BD', 'DB', 'CE', 'EC', 'BE', 'EB']

def solution(m, n, target, array):
    # Edit your code here
    ans = float('inf')
    ans_arr = 'None'
    for arr in array:
        # 计算差异
        dif = 0
        for i in range(m):
            if target[i] + arr[i] not in ban:
                dif += abs(ord(target[i]) - ord(arr[i]))    # ASCII码差值
            else:
                dif = float('inf')
                break

        # 更新最匹配的序列
        if ans > dif:
            ans = dif
            ans_arr = arr
        elif ans != float('inf') and ans == dif:
            ans_arr = ans_arr + ' ' + arr
    return ans_arr

上述代码使用了两层for循环。第一层for循环用于遍历现有序列中的字符串,第二层循环用于遍历字符串索引。代码可以分为两部分,第一部分为计算差异值,第二部分为更新最匹配的序列。

在第一部分中,由于python并不支持字符间的直接计算,经过查阅资料发现可以使用ord函数,该函数以一个字符作为参数,返回字符对应的ASCII码值,这样就可以计算差异值了,注意差异值需要取绝对值。关于“屏蔽词”的处理,本来想用C语言中宏定义的方式直接设定,但查阅资料发现python好像并不支持直接#define,宏定义做起来也怪麻烦的,所以选择直接设置一个全局变量,把所有“屏蔽词”都写进去,注意,正序倒序的“屏蔽词”都要算进去。最后用if-else判断一下即可,python可以直接使用not in来判断是否存在于目标中,减少了额外编写代码的必要,很是方便,而且也很容易看懂。当发现“屏蔽词”后,因为题目的设定是“屏蔽词”的差异值为无穷大,所以相当于这组序列已经没必要再检查了,所以直接break跳出循环,节省时间。

第二部分中,只有if-elif,是很简单的更新判断。第二个elif是后补的,因为提交时遇到样例2,结果出错。本来想把第一个if条件改成>=,碰到多个答案就append。但再仔细想想就会发现,样例3的结果是'None',如果有答案,至少得有一次覆盖掉'None',而且不同的结果之间用空格分开了,所以就再加了一次条件判断。并且这样做的话,即便在找到多个差异值相同的序列后,又遇到了差异值更低的,那么在第一个if中也会直接覆盖掉,并不会影响最终的结果。

最后,提交代码,完成解题。