Longest Window Within Budget
Problem
Each day of a trip has a cost, and neither the costs nor the budget are ever negative. Given the list of daily costs and the budget, return the length of the longest run of consecutive days whose costs add up to at most the budget. If every single day is over budget on its own, return 0.
Examples
Input: costs = [4, 1, 1, 3, 2, 6], budget = 6
Output: 3
Why: days 1-3 cost 1 + 1 + 3 = 5, and no run of four days fits
Input: costs = [0, 0, 2, 0], budget = 0
Output: 2
Why: the two free days at the start are the longest run that costs nothing
Input: costs = [9, 8], budget = 5
Output: 0
Why: edge case, every single day is already over budget
Hints
0 / 3
There are three ways to solve this: re-add every run from scratch, extend a running sum from each start, or never go back at all. Try to reach the third one.
Costs are never negative, so making a run longer can only raise its total and making it shorter can only lower it. That means a start never needs to move backwards.
Keep a window with a left and a right edge and its running total. Move the right edge one day at a time, adding its cost; while the total is over budget, drop days from the left. After each step, the window is the longest valid run ending at the right edge.
Solution
Re-adding every run is O(n³) and extending a running sum from each start is O(n²). Because costs are never negative, the best start for each end only moves forward, so a sliding window visits every day at most twice: once when the right edge adds it and once when the left edge drops it. After shrinking, the window is the longest valid run ending at the current day, and the best of those is the answer. Time is O(n), and space is O(1).
def longest_within(costs, budget):
left = total = best = 0
for right, cost in enumerate(costs):
total += cost
while total > budget: # shrink until the window fits again
total -= costs[left]
left += 1
best = max(best, right - left + 1)
return best
print(longest_within([4, 1, 1, 3, 2, 6], 6)) # -> 3
print(longest_within([0, 0, 2, 0], 0)) # -> 2
print(longest_within([9, 8], 5)) # -> 0Stuck on the idea rather than the code? Comparing Complexities covers it.