Longest run of tasks within a time budget
times[i] is the number of minutes task i takes, always positive. Tasks must be done in
a consecutive run. Return the largest number of consecutive tasks whose total time is at most
budget.
Examples
| Input | Output | Why |
|---|---|---|
times = [3,1,2,7,4,2,1,1,5], budget = 8 | 4 | 4 + 2 + 1 + 1. |
times = [5,5,5], budget = 4 | 0 | No single task fits. |
times = [1,1,1], budget = 10 | 3 |
Constraints
- 1 ≤ n ≤ 10⁵
- 1 ≤ times[i] ≤ 10⁴
- 0 ≤ budget ≤ 10⁹
Hints
Hint 1
Every time is positive. What happens to a run's total when you add a task to it, or remove one from its start?
Hint 2
If the run from lo to hi is over budget, so is every run from lo that ends later.
Hint 3
Grow on the right; while the total exceeds the budget, drop tasks from the left.
Where people lose marks
- Applying the same code to arrays with negative numbers. It gives wrong answers without any error.
- Shrinking past hi when a single task exceeds the budget: the loop must stop when the window is empty.
Dry run on [3,1,2,7,4,2,1,1,5],8
Read it one frame at a time, the way you would trace the code on paper in an interview. Each frame shows the state after the step it describes.
-
Step 1 / 9times30lo/hi1122734425161758total3best1
Add 3: the run 0..0 takes 3. Longest so far: 1.
-
Step 2 / 9times30lo11hi22734425161758total4best2
Add 1: the run 0..1 takes 4. Longest so far: 2.
-
Step 3 / 9times30lo1122hi734425161758total6best3
Add 2: the run 0..2 takes 6. Longest so far: 3.
-
Step 4 / 9times30112273lo/hi4425161758total7best3
Add 7: the run 0..3 takes 13. Over 8, so drop 3, 1, 2 from the left; now 7.
-
Step 5 / 9times3011227344lo/hi25161758total4best3
Add 4: the run 3..4 takes 11. Over 8, so drop 7 from the left; now 4.
-
Step 6 / 9times3011227344lo25hi161758total6best3
Add 2: the run 4..5 takes 6.
-
Step 7 / 9times3011227344lo2516hi1758total7best3
Add 1: the run 4..6 takes 7.
-
Step 8 / 9times3011227344lo251617hi58total8best4
Add 1: the run 4..7 takes 8. Longest so far: 4.
-
Step 9 / 9times30112273442516lo1758hitotal7best4
Add 5: the run 4..8 takes 13. Over 8, so drop 4, 2 from the left; now 7.
How to think about it
Positive times make the total monotone: adding a task increases it, removing one decreases it. So for
each right end hi, the runs within budget are those starting at or after some point, and that
point can only move right as hi does. The window grows by one task each step and drops tasks from
the left while it is over budget; afterwards it is the longest affordable run ending at hi.
The same question with negative numbers allowed breaks this argument, because adding an element can lower the total. The last lesson of the chapter shows what replaces the window then.
Complexity. Time O(n) · Space O(1)
The solution, in six languages
Each one is compiled and run against the test table before it is published. Java is reviewed by hand.
def longest_within(times, budget):
best = lo = total = 0
for hi, t in enumerate(times):
total += t
while total > budget: # over budget: drop tasks from the left
total -= times[lo]
lo += 1
best = max(best, hi - lo + 1) # 0 when the window is empty
return best#include <vector>
#include <algorithm>
int solve(const std::vector<int>& times, long long budget) {
int best = 0, lo = 0;
long long total = 0;
for (int hi = 0; hi < (int)times.size(); ++hi) {
total += times[hi];
while (total > budget) total -= times[lo++]; // over budget: drop from the left
best = std::max(best, hi - lo + 1);
}
return best;
}class Solution {
public int longestWithin(int[] times, long budget) {
int best = 0, lo = 0;
long total = 0;
for (int hi = 0; hi < times.length; hi++) {
total += times[hi];
while (total > budget) total -= times[lo++]; // over budget: drop from the left
best = Math.max(best, hi - lo + 1);
}
return best;
}
}function longestWithin(times, budget) {
let best = 0, lo = 0, total = 0;
for (let hi = 0; hi < times.length; hi++) {
total += times[hi];
while (total > budget) total -= times[lo++]; // over budget: drop from the left
best = Math.max(best, hi - lo + 1);
}
return best;
}fn solve(times: &[i64], budget: i64) -> usize {
let (mut best, mut lo, mut total) = (0usize, 0usize, 0i64);
for hi in 0..times.len() {
total += times[hi];
while total > budget { // over budget: drop from the left
total -= times[lo];
lo += 1;
}
best = best.max(hi + 1 - lo);
}
best
}import Data.Array
-- Fold carrying (window start, total, best). Positive times guarantee the total
-- reaches 0 before lo passes hi, so the shrink always stops inside the array.
solve :: [Int] -> Int -> Int
solve ts budget = third (foldl step (0, 0, 0) (zip [0 ..] ts))
where
arr = listArray (0, length ts - 1) ts
third (_, _, b) = b
step (lo, total, best) (hi, t) =
let (lo', total') = shrink lo (total + t)
in (lo', total', max best (hi - lo' + 1))
shrink lo total
| total > budget = shrink (lo + 1) (total - arr ! lo)
| otherwise = (lo, total)
Variants to try
- The shortest run with total at least the budget: the shrinking happens while the window is valid, not while it is invalid.
- Count the affordable runs: add hi − lo + 1 at each step.
Next problems
Longest run of distinct characters
The window that grows on the right and shrinks on the left, and the invariant that makes it correct.
Open the problem → Medium20 minLongest run with at most k distinct letters
The variable window in its plainest form: grow on the right, and while the window breaks the rule, shrink on the left.
Open the problem → Medium15 minLongest run of ones after k flips
Rephrase "flip at most k zeros" as "a window with at most k zeros", and a counter is all the state you need.
Open the problem →Write a solution and run it against the real test table.
Keep going
Longest run of distinct characters
The window that grows on the right and shrinks on the left, and the invariant that makes it correct.
Open the problem → Medium20 minLongest run with at most k distinct letters
The variable window in its plainest form: grow on the right, and while the window breaks the rule, shrink on the left.
Open the problem → Medium15 minLongest run of ones after k flips
Rephrase "flip at most k zeros" as "a window with at most k zeros", and a counter is all the state you need.
Open the problem → Sliding windowThe pattern behind it
A window that grows on the right, shrinks on the left, and holds an invariant at all times.
Read the pattern →Preparing for a real process? Quant interview preparation, or book a free 20-minute call.