DSAPrep
MediumSliding Window

Maximum Coins From K Consecutive Bags

There are an infinite amount of bags on a number line, one bag for each coordinate. Some of these bags contain coins.

You are given a 2D array coins, where coins[i] = [li, ri, ci] denotes that every bag from li to ri contains ci coins.

The segments that coins contain are non-overlapping.

You are also given an integer k.

Return the maximum amount of coins you can obtain by collecting k consecutive bags.

Example 1

            Input: coins = [[8,10,1],[1,3,2],[5,6,4]], k = 4
            Output: 10
            

            
                Explanation: Selecting bags at positions [3, 4, 5, 6] gives the maximum number of coins: 2 + 0 + 4 + 4 = 10.
              
          

Example 2

            Input: coins = [[1,10,3]], k = 2
            Output: 6
            

            
                Explanation: Selecting bags at positions [1, 2] gives the maximum number of coins: 3 + 3 = 6.
              
          

Constraints

  • 1 <= coins.length <= 10^5
  • 1 <= k <= 10^9
  • coins[i] == [l_i, r_i, c_i]
  • 1 <= l_i <= r_i <= 10^9
  • 1 <= c_i <= 1000
  • The given segments are non-overlapping.
View original on LeetCode ↗

Collecting k consecutive bags is a sliding window, but it slides over coordinates on a number line, not over array indices: a window with left edge L claims every bag from L to L + k − 1. The pattern that unlocks the problem is noticing when a window is worth testing at all. Between two segment boundaries the set of overlapped bags is fixed, so the window total changes at a constant rate as L moves — sliding one bag at a time can never beat the total at one of the run’s own ends. Only two alignments per segment can be optimal: the window’s left edge resting on a segment start, or its right edge resting on a segment end (left edge L = r − k + 1). That observation compresses an unbounded sweep over every coordinate into at most 2N candidate windows, each answered in O(log N) with binary search and prefix sums.

Brute Force: Try Every Window Position

Time O(S·N)Space O(1)

Read the statement literally: for every possible start position on the line, slide the length-k window there, add up the overlap with every segment, and remember the biggest total. It is guaranteed correct because it exhausts every window, but the number of starts is the coordinate span S (the largest bag that can hold coins), and recalculating every window from scratch repeats the overlaps it measured one step earlier.

class Solution:
def maximumCoins(self, coins: list[list[int]], k: int) -> int:
best = 0
# S = the largest bag coordinate that can contain coins.
farthest = max(r for _, r, _ in coins)
for start in range(1, farthest + 1):
end = start + k - 1
total = 0
for l, r, c in coins:
# Overlap of the window [start, end] with bag span [l, r].
lo = max(start, l)
hi = min(end, r)
if lo <= hi:
total += c * (hi - lo + 1)
best = max(best, total)
return best

Why it is unusable at scale: the coordinate span S can reach 10^9 bags and k can reach 10^9 independently, so up to 10^9 window starts each scan all N segments: O(S·N) time in O(1) space. Both dimensions are maxed out in the constraints precisely because the intended solution never walks the line coordinate by coordinate.

Candidate Left Edges with Prefix Sums

OptimalTime O(N log N)Space O(N)

Sort the segments by left edge, then precompute prefix sums of each segment’s full coin total. A window query is then: find the contiguous run of segments the window intersects (two binary searches), take the prefix-sum range, and subtract the (at most two) overhangs — the first segment sticking past the window’s left edge and the last segment sticking past its right edge. The only starts worth querying are the candidate alignments: L = l_i and L = r_i − k + 1 for every segment.

from bisect import bisect_left, bisect_right
class Solution:
def maximumCoins(self, coins: list[list[int]], k: int) -> int:
# Segments never overlap, so once sorted a window only ever
# intersects one contiguous run of segments.
coins.sort()
n = len(coins)
lefts = [s[0] for s in coins]
rights = [s[1] for s in coins]
# Prefix sums of full segment totals: pref[i + 1] sums segments 0..i.
pref = [0] * (n + 1)
for i, (l, r, c) in enumerate(coins):
pref[i + 1] = pref[i] + c * (r - l + 1)
# A start L can only be optimal when a window edge aligns to a
# segment edge: left edge on a segment start, or right edge on a
# segment end (L = r - k + 1).
starts = set()
for l, r, _ in coins:
starts.add(l)
starts.add(max(1, r - k + 1))
best = 0
for L in starts:
R = L + k - 1
# Segments a..b intersect the window [L, R]:
a = bisect_left(rights, L) # first segment ending at or after L
b = bisect_right(lefts, R) - 1 # last segment starting at or before R
if a > b:
continue # the window overlaps no segment
total = pref[b + 1] - pref[a]
if lefts[a] < L: # segment a overhangs the left edge
total -= coins[a][2] * (L - lefts[a])
if rights[b] > R: # segment b overhangs the right edge
total -= coins[b][2] * (rights[b] - R)
best = max(best, total)
return best

Correctness sketch: fix k and slide L. Between two consecutive breakpoints of the form l_i or r_i − k + 1, every overlapping segment keeps the same clip pattern, so the window total is affine in L — its maximum on that run sits at one of the run’s two ends, both candidate starts by construction. Where the window crosses a segment start the total jumps upward exactly at L = l_i, another candidate. Therefore some optimal start is always in the candidate set.

Watch the mechanism — the candidate sweep on example 1. Note how the right-edge alignment at L = 3 takes the lead, and how the straight-line interior slides can never beat their own alignment ends:

plan: 5 candidate starts

Segments — coins per bag

bags 13 · 2
bags 56 · 4
bags 810 · 1

Bags — the k-length window on the line

21
22
23
04
45
46
07
18
19
110
011
·
·
·
·
·
·

▼ candidate starts: a window edge aligned to a segment edge — an interior start can never beat the ends of its own straight slide

1 / 9
currentseencomparingdiscardedresult

Every bag outside a segment holds zero coins, so the whole line reduces to three spans: bags 1..3 hold 2 each, bags 5..6 hold 4 each, and bags 8..10 hold 1 each. With k = 4 the answer is the richest length-4 window. While the left edge L slides, the overlapped set only changes where L crosses a segment boundary, so each segment offers exactly two candidate alignments: L equal to its start, or L = r - k + 1 so the window right edge L + k - 1 sits on its end. That gives five candidates for this input — the marks under the line: L = 1, 3, 5, 7, 8.

Then example 2, a uniform segment that collapses the search into a flat plateau — the two candidate edges already prove the answer for every interior start:

plan: candidates L = 1, 9

Segments — coins per bag

bags 110 · 3

Bags — the k-length window on the line

31
32
33
34
35
36
37
38
39
310
·
·
·
·
·
·
·
·

▼ candidate starts: a window edge aligned to a segment edge — an interior start can never beat the ends of its own straight slide

1 / 5
currentseencomparingdiscardedresult

Example 2 is one segment with no empty interior: bags 1 through 10 hold 3 coins each. The candidates are L = 1 (left edge on the segment start) and L = 9 (right edge on the segment end, since r - k + 1 = 9). Two tests should settle the whole line.

Why it is O(N log N): the sort dominates the running time; after sorting, there are at most 2N candidates, and each one costs two O(log N) binary searches plus O(1) arithmetic — O(N log N) time in total. Space is O(N) for the lefts, rights, and pref arrays. Note the brute-force position span (up to 10^9) never appears anywhere in the algorithm: the number line is only ever touched at segment boundaries.