Codility刷题之旅 - Maximum slice problem

441 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 9 天,点击查看活动详情

今天继续Codility的Lessons部分的第9个主题——Maximum slice problem。

Codility - Lessons - Maximum slice problem

还是按先总结红色部分PDF,然后再完成蓝色的Tasks部分的节奏来~

image.png

PDF Reading Material

  • 前言: 描述了Maximum Slice的概念,即在数组中能够获得最大的sum值的一个从p到q的区间
  • 8.1-Solution with O(n3) time complexity: 双循环实现确定每一个区间,然后再循环统计出区间内的sum值,最后返回所有区间里最大的sum值,复杂度O(n3)。
  • 8.2-Solution with O(n2) time complexity: O(n2)复杂度下的解法,是在双循环确定每个区间后,统计区间sum值的部分借助之前章节讲过的pre_sums思想,来实现常数复杂度级别的sum值计算。
  • 8.3-Solution with O(n) time complexity: O(n)复杂度级别的解法,是创造两个变量max_ending和max_slice,然后只需要从左到右循环数组一次,max_ending记录截至该循环到的元素作为slice末尾时的最大加和,max_slice截至此处max_ending的最大max_ending。最后返回max_slice即可。

Tasks

  • MaxProfit

image.png

也是非常经典的一题,输入是一个股票的价格序列,需要返回的是在这个价格序列里可以获得的最大收益,也就是价格序列中可以获得的最大价格差。

这里解法直接是用的PDF例题里最后O(n)复杂度的解法,只是第一步需要将输入的价格序列A,处理成股价的价差序列diff_A,详情如下:

def solution(A):
    diff_A = [b-a for a,b in zip(A[:-1], A[1:])]
    max_prof = 0
    max_ending = 0
    for a in diff_A:
        max_ending = max(0, max_ending+a) 
        max_prof = max(max_prof, max_ending)
    return max_prof

image.png

  • MaxSliceSum

image.png

本题一开始以为还是PDF里的MaxSliceSum原题,结果复用原解法直接悲剧了,只有不到50%的正确率。仔细看了下题发现和例子并不完全一样,例题里的元素都是正整数,而本题则是可能有负整数的,所以最终要返回的max_slice并不一定sum后大于0,也就是需要拓展下PDF例题里最后给的通用解法。

拓展后的通用解法如下,本质框架还是O(n)复杂度的框架,只是初始化的max_slice和max_ending直接使用A中的第一个元素,循环的时候从A中第二个元素开始,并且计算max_ending的公式不再是max(0,max_ending+a),而是max(a, max_ending+a)

def solution(A):
    max_slice = A[0]
    max_ending = A[0]
    for a in A[1:]:
        max_ending = max(a, max_ending+a) 
        max_slice = max(max_ending, max_slice)
    return max_slice 

image.png

  • MaxDoubleSliceSum

image.png

本题的题目是比较繁琐的,必须要充分理解的基础上才能正确想出解题思路。首先输入还是一个长度为N的Array A,需要返回最大的MaxDoubleSliceSum,而DoubleSlice的定义是对于0<=X<Y<Z<N,分成的两个Array(A[X+1]~A[Y-1], A[Y+1]~A[Z-1])的元素和。

这里根据题目中给的X、Y、Z的大小关系式,及两个Array中元素index的起止,及例题中double slice(3,4,5)=0等信息,我们发现两组Array是可能为空Array的,且此时sum值按0算。并且最小的X+1是1,最大的Z-1是N-2,也就是Array中的index=0,index=N-1的元素一定是不包含在DoubleSliceSum中的。

综合以上信息,可以按照来回计算双max_ending的序列,并且最后彻底调转右侧计算的max_ending序列,和左侧计算的max_ending序列进行合并,来实现依然为在O(n)复杂度下计算出max_sum。并且两次计算max_ending序列的过程中,因为空Array的sum按0算,所以我们的max_ending的计算采用max(0, max_ending+a)的计算方式:

def solution(A):
    # 左侧index=1的元素开始,计算max_ending序列
    max_ending = 0
    l_max_endings = [0]
    for a in A[1:-2]:
        max_ending = max(0, max_ending+a) 
        l_max_endings.append(max_ending) 
    # 右侧index=N-2的元素开始,计算max_ending序列
    max_ending = 0
    r_max_endings = [0]
    for a in A[::-1][1:-2]:
        max_ending = max(0, max_ending+a) 
        r_max_endings.append(max_ending)
    # 计算最终的max_sum
    # print(l_max_endings)
    # print(r_max_endings)
    max_sum = 0
    for l_max, r_max in zip(l_max_endings, r_max_endings[::-1]):
        max_sum = max(l_max+r_max, max_sum)
    return max_sum

image.png