There’s actually a very elegant, very efficient solution using dynamic programming. It takes O(1) space, and O(n) time — this can’t be beat!
Define A
to be the input array (zero-indexed) and B[i]
to be the maximum sum over all sublists ending at, but not including position i
(i.e. all sublists A[j:i]
). Therefore, B[0] = 0
, and B[1] = max(B[0]+A[0], 0)
, B[2] = max(B[1]+A[1], 0)
, B[3] = max(B[2]+A[2], 0)
, and so on. Then, clearly, the solution is given simply by max(B[0], ..., B[n])
.
Since every B
value depends only on the previous B
, we can avoid storing the whole B
array, thus giving us our O(1) space guarantee.
With this approach, mssl
reduces to a very simple loop:
def mssl(l):
best = cur = 0
for i in l:
cur = max(cur + i, 0)
best = max(best, cur)
return best
Demonstration:
>>> mssl([3,4,5])
12
>>> mssl([4, -2, -8, 5, -2, 7, 7, 2, -6, 5])
19
>>> mssl([-2,-3,-5])
0
If you want the start and end slice indices, too, you need to track a few more bits of information (note this is still O(1) space and O(n) time, it’s just a bit hairier):
def mssl(l):
best = cur = 0
curi = starti = besti = 0
for ind, i in enumerate(l):
if cur+i > 0:
cur += i
else: # reset start position
cur, curi = 0, ind+1
if cur > best:
starti, besti, best = curi, ind+1, cur
return starti, besti, best
This returns a tuple (a, b, c)
such that sum(l[a:b]) == c
and c
is maximal:
>>> mssl([4, -2, -8, 5, -2, 7, 7, 2, -6, 5])
(3, 8, 19)
>>> sum([4, -2, -8, 5, -2, 7, 7, 2, -6, 5][3:8])
19