Counting the Hard Way

Imagine you're a weather forecaster computing 7-day rolling averages. Every morning you add today's temperature and subtract the one from 8 days ago. You don't re-sum all 7 days --- that would be absurd. You've been doing sliding window your whole life. You just didn't know it had a name.

Now pretend you're a computer that doesn't know the trick. You have an array of numbers and need to find the largest sum of any 3 consecutive elements. The naive approach: check every group of 3, add them up, keep the biggest. With 8 numbers that is 6 windows and 18 additions. With a thousand numbers? Nearly three thousand additions.

But look closer. When you slide from one group to the next, most of the numbers are the same. You are re-adding values you already counted.

Think about it concretely. Say the array is [2, 7, 1, 8, 3, 5, 6, 4] and you just computed the sum of indices 0 through 2: 2 + 7 + 1 = 10. Now you slide right to indices 1 through 3. You compute 7 + 1 + 8 = 16. But you already knew 7 + 1 = 8 from the previous sum --- the only things that changed were the element that left (the 2) and the element that entered (the 8).

You did three additions when you only needed two operations: subtract the outgoing, add the incoming. That wasted addition is invisible at small scale. At scale, it is the difference between an algorithm that runs in O(n * k) time and one that runs in O(n).

Slide the window yourself and watch what stays, what leaves, and what enters:

L
2
0
7
1
R
1
2
8
3
3
4
5
5
6
6
4
7
window sum =10

What if you could skip the redundant work?

Feel the Waste

You just saw the overlap between consecutive windows. Now you are going to feel it in your fingertips.

You have the array [2, 7, 1, 8, 3, 5, 6, 4] and you need to compute the sum of every window of size 3. That means six windows: [2,7,1], [7,1,8], [1,8,3], and so on. Here is the catch: you have a fixed budget of taps. Every time you touch a cell to include it in your sum, that costs one tap. The brute-force approach uses 3 taps per window --- 18 total. But your budget is tighter than that.

Think about what you noticed on the previous screen. When you slid from [2,7,1] to [7,1,8], the 7 and 1 were already in your window. Re-tapping them would be pure waste. If you could carry that partial sum forward and only tap the new arrival, you would save one tap per window --- five taps saved across six windows.

So you need to figure out where the waste is hiding. Which taps are truly necessary, and which ones are redundant re-additions of values you already know? Your job is to process every window without exceeding the budget. Tap the cells you need, skip the ones you don't. Pay attention to the moment it starts to feel tedious --- that tedium is information. It is your brain noticing the same pattern the algorithm exploits.

Can you do it under budget?

Phase 1: Brute ForceWindow 1 of 6
Budget24 / 24

Tap each cell in the window to count its value.

0
1
2
3
4
5
6
7
tap cells
Window Sum?

The Subtract-Add Trick

You felt it in the last exercise: most of the work was redundant. When you slid from one window to the next, two things changed --- one element left, one element arrived. Everything in the middle stayed put.

So here is the question. You have window [i-k+1 .. i-1] with a known sum. You are about to slide to window [i-k+2 .. i]. Two elements change roles:

When the window slides from position i-1 to position i, the sum changes by exactly two operations. What are they?

Let's trace it concretely. Take the array [2, 7, 1, 8, 3, 5, 6, 4] with k = 3:

  • Window 0: indices [0, 1, 2] = [2, 7, 1]. Sum = 2 + 7 + 1 = 10. This is the initial window --- you compute it the honest way, adding all k elements.
  • Window 1: slide right. Index 0 leaves (the 2), index 3 enters (the 8). Sum = 10 - 2 + 8 = 16. Two operations instead of three additions.
  • Window 2: slide right. Index 1 leaves (the 7), index 4 enters (the 3). Sum = 16 - 7 + 3 = 12. Again, two operations.
  • Window 3: 12 - 1 + 5 = 16. Window 4: 16 - 8 + 6 = 14. Window 5: 14 - 3 + 4 = 15.

Six windows processed with 5 subtract-add pairs and one initial sum. Total operations: k + 2(n - k) = 3 + 10 = 13. The brute-force approach would have done 6 * 3 = 18. That gap widens dramatically with larger k. For k = 1000 on a million-element array, brute force does roughly a billion additions. The subtract-add trick does about two million. Same answer, three orders of magnitude less work.

Distilled into one line of code:

1
windowSum = windowSum - arr[i - k] + arr[i];

And here is the full pattern for LC 643 (Maximum Average Subarray I):

1
function findMaxAverage(nums: number[], k: number): number {
2
  let windowSum = 0;
3
  for (let i = 0; i < k; i++) windowSum += nums[i];
4
5
  let maxSum = windowSum;
6
  for (let i = k; i < nums.length; i++) {
7
    windowSum += nums[i] - nums[i - k];
8
    maxSum = Math.max(maxSum, windowSum);
9
  }
10
  return maxSum / k;
11
}

The first loop builds the initial window sum for elements 0 through k - 1. The second loop slides one position at a time. At each step, nums[i] enters on the right and nums[i - k] exits on the left. The difference nums[i] - nums[i - k] is the net change to the sum.

Notice there is no inner loop. Every element is visited exactly once. That is O(n) regardless of the window size, whether k is 3 or 3,000. Consecutive windows share almost all their elements --- the formula is the mechanical consequence of that fact. Every time you see a problem that asks about fixed-size contiguous groups, this subtract-add trick is your first move.