Codility刷题之旅 - Exercise1 - 2015 Contest

271 阅读5分钟

完成Codility的Lessons部分的全部课程后,我们接着进入Codility的Exercise部分。

这部分截至我开始解答时,一共是9个Part,我们还是顺序进行问题的解答。这部分对比Lessons部分,并没有PDF Material,即需要完全依靠自己的积累进行问题解答了。

Exercise1 - 2015 Contest

LongestPassword:Given a string containing words, find the longest word that satisfies specific conditions.

image.png

问题概述:本题输入是一个一共有N个字符的长String S,其中包含数字0-9、字母a-z & A-Z、特殊字符和空格。其中包含的空格可以将S分为多个words,K个空格就是分成K+1个words。需要返回的是一个值,代表拆分的words中,符合条件的words的最长长度。而符合条件的word,需要符合的是以下三点: - 只包含数字和字母 - 包含偶数位的字母 - 包含奇数位的数字

解法概述:解法还是很简单的,主要是用到了split()进行words拆分,用到了isalpha()和isdigit()进行数字和字母判断。其他的就是简单的循环判断即可。具体解法如下,应该一看就懂了:

def solution(S):
    items = S.split(' ')
    longest = -1    
    for item in items:
        num_of_letters = 0
        num_of_digits = 0
        num_of_others = 0
        for letter in item:
            if letter.isalpha():    num_of_letters += 1
            elif letter.isdigit():  num_of_digits += 1
            else:                   num_of_others += 1
        if num_of_digits % 2==1 and num_of_letters % 2==0 and num_of_others==0:
            longest = max(longest, num_of_letters + num_of_digits)
    return longest

image.png

FloodDepth:Find the maximum depth of water in mountains after a huge rainfall.

image.png

问题概述:输入是一个包含N个整数的Array A,代表的是从一座山的横截面看过去,山从index=0~index=len(A)-1位置处的高度。需要返回的,是Array A代表的这座山的形状下,可以承载的最大水池的深度。

解法概述:本题算是比较经典的题了,印象中之前在刷LeetCode上也刷到过。这个需求的解——最大水池深度,取决于Array A代表的山的形状下,能够得到的最大高低落差,并且这个落差必须在水池两侧都有。 这里我们用Codility的Lessons部分的Caterpillar思想来应对:从山的左侧和山的右侧分别用Caterpillar思想扫一遍,计算并记录下每个位置的max_depth,然后将左右两次扫到的同一位置的max_depth取最小,就是真正的每个位置的最大水深。最后再对所有位置的max_depth取一次max(),也就是这道题的解了。只要想清楚解题思路,本题的代码实现上其实没有多大难度:

def solution(A):
    max_depths1 = len(A) * [0]
    max_depths2 = len(A) * [0]
    # 从左到右
    begin = 0
    max_H = 0
    while begin<=len(A)-1:
        max_depths1[begin] = max(max_H - A[begin], max_depths1[begin])
        max_H = max(A[begin], max_H)
        begin += 1
    # 从右到左
    end = len(A)-1
    max_H = 0
    while end>=0:
        max_depths2[end] = max(max_H - A[end], max_depths2[end])
        max_H = max(A[end], max_H)
        end -= 1
    # 合并,每个位置取最小值,然后再看max(depth)
    max_depth = [min(d1,d2) for d1,d2 in zip(max_depths1, max_depths2)]
    return max(max_depth)

image.png

SlalomSkiing:Given a sequence, find the longest subsequence that can be decomposed into at most three monotonic parts.

image.png

image.png

问题概述:输入是一个有N个正整数的Arrat A,其中每个元素,代表从雪山山顶到山脚的一共N个滑雪门的位置,比如A[0]=15,就代表离山顶最近的第一个滑雪门,在位置=15处。而我们将作为滑雪运动员,可以选择从山顶的任何位置开始向下滑动,我们可以选择向左侧滑,也可以选择向右侧滑,并且在途中可以进行至多两次转换方向的操作。

在一个方向的滑动中,我们可以根据Array A中顺序的门的位置,选择穿过位置和我们行进方向相符的多个门(比如我们是向右滑动,只要Array A中后边门位置>前边门位置,我们就可以同时穿过两个门)。

最终我们需要的输出,是在上述的规则下,我们从山顶到山底,最多可以通过多少道门。

解法概述:这里的解法在技术上是用到了二分查找,但是前期构思道寻解的过程是十分困难的,这里我觉得最精妙的一种思想,就是codesays这里提到的镜像世界,将原题目的最多两次转向的条件构造为两个镜像世界,这样转化后,问题的寻解过程就变成了“在拼接而成的新Array里找到从山顶到山底单向一次性最多可以通过多少门”的路线问题:codesays.com/2016/soluti…

问题的solution函数,我就直接参考了上边链接里的解法,因为自己确实有点想不出来。solution中的注释已经写的很清楚了,只能说镜像世界的思路下,问题复杂度真的降低了好多:

def LongestIncreasingSubsequence(seq):
    ''' The classic dynamic programming solution for longest increasing subsequence. More details could be found:
        1. https://en.wikipedia.org/wiki/Longest_increasing_subsequence
        2. http://www.geeksforgeeks.org/dynamic-programming-set-3-longest-increasing-subsequence/
        3. http://stackoverflow.com/questions/3992697/longest-increasing-subsequence
    '''
    # smallest_end_value[i] = j means, for all i-length increasing subsequence, the minmum value of their last elements is j.
    smallest_end_value = [None] * (len(seq) + 1)
    smallest_end_value[0] = -1 # The first element (with index 0) is a filler and never used.
    
    lic_length = 0 # The length of the longest increasing subsequence.
    for i in range(len(seq)):
        # Binary search: we want the index j such that:
        #     1. smallest_end_value[j-1] < seq[i]
        #     2. (smallest_end_value[j] > seq[i]) OR (smallest_end_value[j] == None)
        lower = 0   # index j
        upper = lic_length
        while lower <= upper:
            mid = (upper + lower) // 2
            if seq[i] < smallest_end_value[mid]:
                upper = mid - 1
            elif seq[i] > smallest_end_value[mid]:
                lower = mid + 1
            else:
                raise "Should never happen: the elements of A are all distinct"
        if smallest_end_value[lower] == None:
            smallest_end_value[lower] = seq[i]
            lic_length += 1
        else:
            smallest_end_value[lower] = min(smallest_end_value[lower], seq[i])
    return lic_length

def solution(A):
    # We are solving this question by creating two mirrors.
    bound = max(A) + 1
    multiverse = []
    for point in A:
        multiverse.append(bound * 2 + point) # The point in the double-mirror universe.
        multiverse.append(bound * 2 - point) # The point in the mirror universe.
        multiverse.append(point) # The point in the original universe.
    return LongestIncreasingSubsequence(multiverse)

image.png