Many array problems ask about contiguous segments: the longest one with some property, the number of segments below a limit, and so on. Checking every segment takes O(n2) time. The two pointers technique often brings that down to O(n) by moving a window across the array instead of rebuilding it from scratch.
The problem
Valera has t free minutes and a row of n books. Book i takes ai minutes to read. He picks a starting book and reads books in order until he runs out of time (he never stops halfway through a book). What is the largest number of books he can read? This is Codeforces 279B – Books, with n up to 105.
In other words: find the longest contiguous segment whose sum is at most t.
Brute force first
Try every start l, extend r while the sum stays within t, and remember the best length. That is correct but O(n2) in the worst case, about 1010 steps here.
The key observation
Suppose the best window that ends at position r starts at position l. When we move to r + 1, the best window ending there cannot start before l: if it did, it would contain the window [l − 1, r], whose sum is already too large (all times are positive). So the left end only ever moves right.
That gives the algorithm:
- Move the right pointer one step and add the new book to the running sum.
- While the sum is more than t, remove the leftmost book and move the left pointer right.
- The window [left, right] is now the longest valid one ending at right. Update the answer.
Code (C++)
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
long long t;
cin >> n >> t;
vector<long long> a(n);
for (auto &x : a) cin >> x;
int best = 0;
long long sum = 0; // sum of a[left..right]
int left = 0;
for (int right = 0; right < n; right++) {
sum += a[right];
while (sum > t) { // window too expensive: shrink from the left
sum -= a[left];
left++;
}
best = max(best, right - left + 1);
}
cout << best << '\n';
return 0;
}
Walking through an example
t = 5, books 3 1 2 1.
- right = 0: window [3], sum 3. Best = 1.
- right = 1: window [3, 1], sum 4. Best = 2.
- right = 2: sum 6 > 5, so drop the 3. Window [1, 2], sum 3. Best stays 2.
- right = 3: window [1, 2, 1], sum 4. Best = 3.
Why it is O(n)
There is a while loop inside a for loop, which looks quadratic. But each pointer only moves forward, and each moves at most n times in total. Counting all the steps of both pointers gives at most 2n, so the whole algorithm is O(n) time and O(1) extra memory.
When two pointers works, and when it does not
The technique relies on the window property being monotonic: shrinking a valid window keeps it valid, and growing an invalid one keeps it invalid. With positive numbers and “sum ≤ t”, that holds.
It breaks if the array can contain negative numbers. Then adding an element can make the sum smaller, and the left pointer might need to move back. Those problems usually need prefix sums with sorting or a data structure instead.
Another common form: two pointers on a sorted array
To find two numbers in a sorted array that add up to x, start one pointer at each end. If their sum is too small, move the left pointer right; if too large, move the right pointer left. Each step rules out one element for good, so it is also O(n) after sorting. CSES 1640 – Sum of Two Values is a good exercise for this version.
Practice problems
- Codeforces 279B – Books: the problem in this post.
- Codeforces 676C – Vasya and String: longest window with at most k characters changed.
- CSES 1640 – Sum of Two Values: the sorted-array version.
Two pointers pairs naturally with run-counting problems like 266A, where you also compare each element with what came before.