Versioned Friend Recommendations
Reported by candidates from OpenAI's online assessment. Pattern, common pitfall, and the honest play if you blank under the timer.
The OpenAI OA reported in July 2025 looks like a social network question, but it's really a versioned adjacency store with a top-k query on top. If you've got an invite for this one, expect to build snapshots without copying the graph, then rank two-hop candidates by shared middle users. It's design plus a counting pass plus a bounded heap. Nothing exotic, but the pieces have to line up cleanly under pressure. Read the constraints first: n is 500 and recommends are capped at 200. StealthCoder sits invisibly as a safety net if you blank mid-assessment, but the plan below is enough to walk in with.
The problem
A directed social graph has n users numbered from 0 to n - 1. A relationship u -> v means that user u follows user v. Process relationship updates, immutable snapshots, and historical recommendation queries. Every snapshot includes all earlier updates, and later updates never change an earlier snapshot. Operations FOLLOW u v activates u -> v. The relationship is inactive immediately before the command. UNFOLLOW u v deactivates u -> v. The relationship is active immediately before the command. SNAPSHOT creates the next snapshot. IDs start at 0. Append the ID as a decimal string. RECOMMEND user snapshot_id k appends up to k recommendations computed from that snapshot. Recommendation Rule A candidate must be different from user, must not already be followed by user, and must be reachable by at least one two-hop path user -> middle -> candidate at the requested snapshot. The candidate's score is the number of distinct active middle users that form such a path. Rank candidates by score descending, then user ID ascending. Return the first k candidate IDs. Serialize each recommendation list as a string containing IDs inside brackets, separated by commas and no spaces. Serialize an empty list as []. Only SNAPSHOT and RECOMMEND produce output. Return those strings in command order. Historical Storage For this exercise, assume each directed relationship is stored as a change history and the whole graph is not copied for each snapshot. Use binary search on that authored history and a bounded heap for recommendation selection. Function recommendAtSnapshots(n: int, operations: String[]) → String[] Examples Example 1 n = 5 operations = ["FOLLOW 0 1","FOLLOW 0 2","FOLLOW 1 3","FOLLOW 2 3","FOLLOW 2 4","SNAPSHOT","RECOMMEND 0 0 2"] return = ["0","[3,4]"] User 3 has score 2 through middle users 1 and 2. User 4 has score 1 through user 2. Example 2 n = 6 operations = ["FOLLOW 0 1","FOLLOW 0 2","FOLLOW 1 3","FOLLOW 2 4","SNAPSHOT","RECOMMEND 0 0 5"] return = ["0","[3,4]"] Users 3 and 4 each have score 1. The smaller user ID breaks the tie. Example 3 n = 5 operations = ["FOLLOW 0 1","FOLLOW 1 2","SNAPSHOT","FOLLOW 0 2","FOLLOW 1 3","SNAPSHOT","RECOMMEND 0 0 3","RECOMMEND 0 1 3"] return = ["0","1","[2]","[3]"] At snapshot 0, user 2 is a two-hop candidate. At snapshot 1, user 0 already follows 2, so 2 is excluded and 3 is recommended. Constraints 1 <= n <= 500 1 <= operations.length <= 10000 There are at most 200 RECOMMEND commands. 0 <= u, v, user < n and u != v 1 <= k <= n Every FOLLOW targets an inactive relationship. Every UNFOLLOW targets an active relationship. Every recommendation references a snapshot that has already been created. Commands contain single spaces between tokens. At least one command produces output.
Reported by candidates. Source: FastPrep
Pattern and pitfall
The trick is the change history. For each directed edge, store a sorted list of snapshot-versions where it flipped on or off, or per user store (snapshot_id, neighbor, active) events. A snapshot is just a counter. To answer a query at snapshot s, binary search each relevant history for the last event at or before s. Then for the user, find the active followees (the middles). For each middle, scan its active followees, skip the user and anyone the user already follows, and increment a count per candidate. Distinct middles means each middle contributes at most once per candidate, which falls out naturally since edges are unique. Push (score, id) into a size-k heap, or just sort, since n is 500. Pitfalls: counting a candidate the user already follows, forgetting to exclude the user, tie-breaking on ID ascending, and printing [] for empty. If you freeze mid-OA, StealthCoder is the hedge, but know the structure cold.
If you see this problem in your OA tomorrow, the play is to recognize the pattern in 30 seconds. StealthCoder buys you that recognition.
You can drill Versioned Friend Recommendations cold, or you can hedge it. StealthCoder runs invisibly during screen share and surfaces a working solution in under 2 seconds. The proctor sees the IDE. They don't see what's behind it. Built by an Amazon engineer who passed his OA cold and still thinks the filter is broken.
Get StealthCoderRelated leaked OAs
You've seen the question.
Make sure you actually pass OpenAI's OA.
OpenAI reuses patterns across OAs. Built by an Amazon engineer who passed his OA cold and still thinks the filter is broken. Works on HackerRank, CodeSignal, CoderPad, and Karat.
Versioned Friend Recommendations FAQ
How hard is the OpenAI versioned friend recommendations question really?+
Medium in difficulty, but long to implement. No single step is hard. The risk is parsing commands, versioned lookups, and ranking all correct in one pass. With n at 500 and 200 recommends, brute force over two hops per query is fast enough.
What's the core trick?+
Don't copy the graph per snapshot. Keep a change history per edge or per user, and binary search it for the state at the requested snapshot. Then count two-hop paths with a hash map keyed by candidate and rank the results.
Do I need a heap, or is sorting fine?+
The prompt suggests a bounded heap, but with n at most 500, sorting all candidates by score descending then ID ascending works too. A heap of size k is cleaner if you want to match the spec. Either gives the same output.
What edge cases break most solutions?+
Including the user themselves, including someone they already follow, wrong tie-breaks, and an empty result that must print as []. Also remember SNAPSHOT outputs its ID as a string, and an UNFOLLOW must be reflected only in later snapshots, never earlier ones.
How do I prepare for this in 48 hours?+
Practice a versioned key-value store with binary search on timestamps, then a two-hop neighbor count with a hash map. Write the command parser once. Run all three examples by hand, especially example 3, where a later follow removes a candidate.