I Can Solve the Question. So Why Do I Freeze on the Follow-Up?

One familiar problem, three changed requirements, and a practice routine for the moment your memorized answer runs out.
Getting the first solution right can make the next question feel weirdly personal. You were explaining everything so confidently a minute ago, and now someone has changed one requirement and your lovely little hash map suddenly needs a lawyer.
I don’t think the useful response is to panic and add another fifty problems to the queue. Sometimes the missing practice is much smaller. We learned how to reach an answer, but stopped before learning which assumptions were holding it up.
Try this before reading further. You have an array of numbers and need two different positions whose values add up to a target. You know the usual hash-map approach. Now the task changes, and you must count every valid pair of positions instead of returning one pair.
For [1, 1, 3, 3] with a target of 4, is the answer one, two, or four? More importantly, what information does your original solution throw away?
That is the small gap we’re going to practise. No new stack, enormous roadmap, or heroic weekend required.
Your first solution has a contract, even if you never wrote it down
For our starting version, assume an unsorted array, exactly one valid pair, and permission to use extra memory. Return the two indices without reusing an element.
Scan from left to right. Before storing the current value, look for its complement among the values you have already seen. If it is there, you have your pair. With ordinary hash-map assumptions, that takes expected O(n) time and O(n) extra space.
Notice what makes the solution comfortable. You can stop after the first match. One saved index per value is enough. Keeping previous values in memory is allowed.
Those details are the handles a follow-up can pull on. I prefer writing them beside the solution because otherwise the code starts to look more universal than it actually is.
Before changing any code, name what changed, preserve what still works, and find a tiny example that exposes the difference. That gives you something useful to do while your brain catches up.
Count every pair, and the missing information becomes obvious
Back to [1, 1, 3, 3]. Label the positions 0, 1, 2, 3 and the valid pairs are (0,2), (0,3), (1,2) and (1,3). We are counting pairs of positions with the earlier index first, so the answer is four.
If the interviewer means unique pairs of values instead, the answer is one. That clarification belongs before the implementation. You can write perfectly good code for the wrong definition of a pair, which is a very annoying way to spend ten minutes.
For position pairs, a map that remembers only one index for each value has forgotten something important. When the first 3 arrives, it can pair with both earlier 1s.
We still need the complement lookup and the left-to-right scan. What changes is the information in the map. Store a frequency for each previous value, and add the complement’s frequency to the answer.
def count_pairs(nums, target): seen = {} total = 0 for value in nums: total += seen.get(target - value, 0) seen[value] = seen.get(value, 0) + 1 return total Look up first, then increment. That order means the map contains only earlier positions, so the current element cannot pair with itself.
After the two 1s, the map holds a frequency of two. Each subsequent 3 contributes two more pairs. For [2, 2, 2] and target 4, the contributions are zero, one and two, giving three pairs rather than nine.
Expected running time remains O(n), with O(u) map space for u distinct values. Returning all the pairs would be a different requirement again. The output itself can contain O(n²) pairs, so counting them and listing them cannot share the same blanket performance promise.
This is why I like practising with tiny inputs. Four numbers revealed the ambiguity, the missing state and the update order. A large random test might give you a red result without explaining any of that.
Sorted input changes the opportunity, not just the example
Reset to finding one pair. This time, the input is already sorted and you are asked to use constant extra space. Suppose [1, 2, 4, 7] has target 6.
Place one pointer at each end. The first sum is eight, which is too large, so move the right pointer left. Now the sum is five, so move the left pointer right. Two plus four gives the target.
The explanation matters more than the pointer vocabulary. If the sum is too small, pairing the current left value with any smaller right value cannot help. Sorted order lets us safely discard that left position. The reverse argument applies when the sum is too large.
We get O(n) time and O(1) auxiliary space because the array is already sorted. If you sort an originally unsorted input yourself, you must account for sorting costs and preserve original indices when the output requires them.
So the useful response to this follow-up is not immediately reaching for a different memorized template. First identify the new guarantee, then explain which search space it lets you eliminate.
A stream changes what you are allowed to forget
Now imagine numbers arriving one at a time, and you need the running count of all pairs seen so far. The frequency-map version still works. Each new value contributes the number of matching earlier values before its own count is added.
But there is a catch worth saying out loud. Streaming does not automatically mean constant memory. With unrestricted values, this map can keep growing as new distinct values arrive.
If the follow-up also limits memory, clarify the value range and whether only a recent window matters. A small bounded range can support a fixed-size frequency array. A last-k-items window needs expiration rules and bookkeeping. Those are changed contracts, not free optimizations for the original all-history problem.
This is a useful place to be calm about uncertainty. You can explain that the current approach remains correct, identify its growing memory cost, and ask which restriction the revised task actually provides.
Give yourself a recovery routine before you need one
When a follow-up feels slippery, take it through four moves. Think of this as a practice scaffold, not a script you have to perform word for word.
- Restate the changed requirement. For the counting version, clarify whether different positions count separately. Keep unchanged requirements visible too.
- Find the smallest revealing input. Repeated values expose the difference between one saved index and a frequency. Pick an example whose answer you can justify without running code.
- Keep the reasoning that survived. Complement lookup still helps. Stopping at the first match no longer does. Change the information you retain before rewriting everything.
- Check the revised cost and failure case. Explain time, memory and output size, then test the edge that motivated the change.
In a conversation, those moves can flow naturally. We now need every pair of positions, so duplicate values matter. The complement lookup is still useful, but one index per value loses matches. I’ll store frequencies and check before adding the current value so an element never matches itself.
That explanation gives the interviewer a clear line to follow, even before you finish typing. It also gives you a way back into the problem that does not depend on instantly recognizing its name.
Make your next practice session slightly less comfortable
Spend twenty minutes on a problem you already understand. Familiarity is useful here because it leaves room to practise the change rather than spending the entire session discovering the baseline.
For the first five minutes, solve the original version and write down its input guarantees, required output and memory allowance. During the next five, change exactly one requirement and construct a small example that makes the old answer insufficient.
Use the next seven minutes to adapt the approach, explain what you kept and test the revised behavior. Spend the final three writing one sentence about the assumption you missed. That sentence is more useful than a vague note to revise hashing again.
You can start with this Two Sum practice , then add the counting variation yourself. The linked problem is the baseline; the changed requirements in this essay are our practice extensions.
On the next session, choose a different kind of change. Make the input sorted, restrict memory, or ask for a running result. Don’t pile them together immediately, because then it becomes harder to tell which assumption caused the difficulty.
Judge the session by whether you can explain the change and defend the new solution. Finishing quickly is lovely, but recognizing why your original answer stopped being enough is the skill we came to practise.
I want my practice notes to contain more than solutions I can reproduce when the question behaves itself. I want them to show what I can keep when it changes, what I need to question, and how I can find my footing again.
Before closing your next solved problem, give it one follow up and stay for the slightly uncomfortable part. That is where the next practice session begins.
~ Emily



