In a triple-sum enumeration on sorted input (fix a[i], converge lo/hi on the rest), why does skipping i when a[i] == a[i+1] drop valid triples?
answer
- which occurrence does the skip keep?
- the fixed element's partners live to its right
- processing the last copy leaves no copies as partners
- skip against the previous element, after processing
- on a match, both pointers pass their duplicates
basics
~20 sSkipping while the fixed value equals the next one processes only the last copy, whose search window holds no more copies — so triples needing two equal values are lost. Skip against the previous element instead, after the first occurrence is processed.
solid answer
~50 sThe test `a[i] == a[i+1]` skips the first occurrences of each repeated value and processes only its last copy. But the fixed element's partners live strictly to its right (`lo` starts at `i + 1`), so when the last copy of value v is the one processed, no copies of v remain in the window — any triple needing two copies, like (5, 5, 9) for target 19, is never generated. The correct guard is the mirror: skip i when `i > 0 and a[i] == a[i-1]`, which processes the first occurrence with all its duplicates still ahead of it and skips only the redundant re-runs. The same discipline applies on a match inside the window: record it, then advance both pointers past all equal values — advancing just one re-emits the same value pair.
code
pseudocode · 15 linessort(a)
for i in 0..n-3
if a[i] == a[i+1]
continue // intended: avoid duplicate triples
lo = i + 1
hi = n - 1
while lo < hi
s = a[i] + a[lo] + a[hi]
if s == target
record(a[i], a[lo], a[hi])
...
else if s < target
lo = lo + 1
else
hi = hi - 1go deeper
Know that duplicate-heavy sorted input is where naive pair and triple enumeration breaks, and that skip-duplicates logic has a right and a wrong placement. Be able to walk a tiny example like [2, 5, 5, 5, 9].
Be ready to trace both skip placements over a run of equal values and show exactly which triple disappears, then state the two-sided rule: skip after processing the first occurrence, and advance both pointers past duplicates on a match.
An interviewer expects you to catch this bug in code review: probe with a duplicate-heavy test vector, and pin down the output contract — distinct value tuples versus index tuples — before judging any skip logic.
Use this as a case study in specification-first review: most duplicate bugs are an undecided output contract, not a coding slip. Push teams to state whether results are value-sets or index-sets before anyone optimizes the enumeration.
## The setting A debate stage is being seated from a roster sorted by influence score, and production wants trios whose scores sum to a neutral target. **Duplicate-heavy input** is the norm — many guests share a score — and duplicates are exactly where naive converging-pointer enumeration goes wrong, in two distinct places. ## The enumeration skeleton 1. Sort the input. 2. For each index `i` (the fixed element), run a converging scan with `lo = i + 1`, `hi = n - 1`, looking for `a[lo] + a[hi] == T - a[i]`. To emit *distinct value triples* rather than repeats, duplicate values must be skipped at both levels — and the placement of those skips is the whole question. ## Bug site one: skipping the fixed element too early The tempting guard `if a[i] == a[i+1] then continue` skips *forward* through a run of equal values and processes only its **last** occurrence. But the fixed element's candidate partners live strictly to its right. Process the last copy of value `v` and no copies of `v` remain in the window — any triple needing two copies, `(v, v, w)`, becomes unreachable. Trace `a = [2, 5, 5, 5, 9]`, target 19. The only qualifying triple is `(5, 5, 9)`. - **With the buggy guard**, `i` skips indices 1 and 2 (each 5 equals the next) and fixes the 5 at index 3; the window is just `{9}`, which contains no pair summing to 14. Output: nothing. - **With the correct guard** — `if i > 0 and a[i] == a[i-1] then continue` — the *first* 5 (index 1) is processed with window `{5, 5, 9}`, and `5 + 9 = 14` yields the triple; the later copies are then skipped as redundant re-runs of the same fixed value. The rule in one line: **skip duplicates after processing the first occurrence, never before it.** The backward-looking test keeps answers that use repeated values while still eliminating repeated work. ## Bug site two: the match step inside the window When `a[lo] + a[hi]` matches and the pair is recorded, advancing only one pointer leaves the other sitting on the same value; the next iteration re-derives the same *value* pair through a different index. For distinct-value output, advance **both** pointers past all copies of their current values after recording. That skip is safe for the same reason the outer one is: it happens after the first occurrence of the pair has been processed. ## The sandwich fact If a match occurs with `a[lo] == a[hi]` and `lo < hi`, sorted order pins every element strictly between them to that same value. The window then holds exactly one distinct value pair but `k(k-1)/2` index pairs across `k` copies. This forces a specification question that should be settled before any skip logic is written: is the output *distinct value tuples* or *index tuples*? - **For value tuples**, record once and close the window. - **For index tuples**, count the `k choose 2` combinations arithmetically rather than stepping pointers one at a time. Many "duplicate bugs" found in review turn out to be this contract left undecided. ## Complexity is unchanged Every skip advances an index that would advance anyway, so the enumeration remains O(n²) after an O(n log n) sort, with O(1) auxiliary space beyond the output itself. Duplicate handling is purely a **correctness concern** — no lost answers, no repeated answers — not a performance lever. ## What interviewers listen for Anyone can say "skip duplicates." The signal is placement and direction: - the backward-comparing skip after processing at the outer level; - the both-pointer skip after recording at the inner level; - and the sandwich observation when equal values meet. And the practical habit: bring a duplicate-heavy vector like `[2, 5, 5, 5, 9]` to any review of this code — it kills the wrong version instantly.
- During the inner scan you hit a match where a[lo] == a[hi] with lo < hi. What does sortedness tell you about the elements between them?They are all equal to that same value — sorted order sandwiches them. The window holds exactly one distinct value pair but k(k-1)/2 index pairs across k copies. So decide the output contract first: distinct value pairs mean record once and close the window; index pairs mean count k choose 2 arithmetically instead of stepping one position at a time.
- Does duplicate-skipping change the asymptotic complexity of the enumeration?No. Each skip advances an index that would advance anyway, so the work stays O(n) per fixed element and O(n^2) overall after the O(n log n) sort, with O(1) auxiliary space beyond output. Skip placement affects only correctness — whether answers are lost or repeated — never the bound.
saying these in an interview costs you the question
- Skips duplicate fixed values before processing their first occurrence
- Advances only one pointer after recording a matching pair
- Assumes emitting duplicates and deduplicating afterwards is free
- Cannot say what sorted order implies between equal pointer values