Python HackerRank 刷题 Maximum Subarray Sum

题目地址: https://www.hackerrank.com/challenges/maximum-subarray-sum/problem?isFullScreen=false

简述:在给定正整数组中,找到任意连续子数组求和后对M取余的最大值。

栗子

当a = [3,2,7,4] 且 m=7 时,返回6。因为子数组[2,7,4]求和后对7取余可取到最大值6。

难度:Hard

解题

先正常搞。直接切片一行搞定。结果肯定是无法通过全部测试的。原因主要是因为切片的执行时间太长,其次是因为有2个for迭代。

def maximumSum(a, m):
    return max([sum(a[i:j]) % m for i in range(len(a)) for j in range(i+1,len(a)+1)])

通过分析子数组情况我们可知,例子中子数组 [3,2,7] 和子数组 [3,2] 存在交集或者说重复的部分。若我们已经知道子数组 [3,2] 对m求余的值为R[3,2],则 [3,2,7] 对m取余的值 R[3,2,7] = (R[3,2] + 7) % m。知道这一点以后,我们便可以构建数组a的prefix sum 来解决这个问题。

接上面的例子,数组a的对m求余的prefix sum数组presum_a = [3,5,5,2],其中

presum_a[1] = (presum_a[0] + a[1]) % m = (3+2)%7 = 5

presum_a[2] = (presum_a[1] + a[2]) % m = (5+7)%7 = 5

当我们有了presum_a以后,事情就好办了。由于求余运算的性质,presum_a中每个元素取值范围为 [0, 7) ,并且通过观察我们可知presum_a[3] < presum_a[0],可知a的子数组[2,7,4]是一个值得考虑的对象。因为当j > i时,presum_a中任意两个元素差值的绝对值abs(presum_a[i] - presum_a[j])就代表a中从i到j的子数组求和并对m取余的值,与m之间的差值,即

abs(m - (a[i] +... + a[j])%m)

若这个差值越小,则代表我们我们求和取余的值越大。因此,由于abs(presum_a[3] - presum_a[0]) = 1,这代表a[1]到a[3]至少加了一个6或者6的倍数,而6是对7求余能够得到的最大值。

那么问题就简单啦,根据上面描述的例子,当我们只需要找到presum_a中任意两元素差值绝对值的最小值d = min(abs(presum_a[i] - presum_a[j])),并且拿m - d与presum_a中的最大值作比较max(max(presum_a), m-d),选择两者中大的那一个即可。

def maximumSum(a, m):
    # 原始数组a的值在做完prefix sum后没有价值了,不需要保存,因此可直接在原数组上滚。
    a[0] = a[0]%m
    for i in range(1,len(a)):
        a[i] = (a[i - 1] + a[i])%m

    max_d = max(a)                              # 拿到滚出来的最大值

    a = list(zip(a,[i for i in range(len(a))])) # 把元素index贴上,用来判断j > i
    a.sort()                                    # 来一手排序,减少找任意两元素差值最小值的计算量
    d = min([abs(a[i][0]-a[i+1][0]) for i in range(len(a)-1) if a[i][1] > a[i+1][1]])
    return max(max_d,m-d)

程序整体除了排序O(nlogn)时间,其余O(n)时间,因此程序整体O(nlogn)时间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值