The round comes in three graded parts, submitted one at a time. Reports are consistent that the clock is tight, so finish the first two quickly.
Part 1 — most frequent call path
You are given a list of trace lines. -> funcName means a function was entered; <- funcName means it returned. When a function is entered, the current full call path is every function then on the stack, joined by ->.
Count how often each full call path occurs, counted on each entry event, and return the most frequent path. For empty input, return "".
traces = [
"-> main",
"-> handleEvents",
"-> handleClickEvent",
"<- handleClickEvent",
"-> handleClickEvent",
"<- handleClickEvent",
"<- handleEvents",
"<- main",
]
Counting, on each entry:
main -> 1
main->handleEvents -> 1
main->handleEvents->handleClickEvent -> 2
Output: "main->handleEvents->handleClickEvent"
Part 2 — tie-breaking
If several paths share the highest frequency, prefer the deeper path (more functions). If the depth is also tied, prefer the path that reached that frequency first, scanning left to right.
Watch this one: the prompt's written rule says "the one that appeared first", but its own worked example resolves the tie the other way, and the two rules give different answers. In the example below handleKeyEvent appears first, handleClickEvent reaches the winning count first, and the stated output is the click path — so the example is the contract. Confirm which the interviewer means before coding.
traces = [
"-> main", "-> handleEvents",
"-> handleKeyEvent", "<- handleKeyEvent",
"-> handleClickEvent", "<- handleClickEvent",
"-> handleClickEvent", "<- handleClickEvent",
"-> handleKeyEvent", "<- handleKeyEvent",
"<- handleEvents", "<- main",
]
Both main->handleEvents->handleClickEvent and main->handleEvents->handleKeyEvent occur twice at the same depth.
Output: "main->handleEvents->handleClickEvent"
Part 3 — multiple threads
Each trace line now carries a thread_id and the lines are interleaved. Return the most frequent call path per thread. The lines merely carry a thread_id; no actual multithreading is required, and the interviewer will ask you to reuse your Part 1 function rather than rewrite the logic.
Constraints
0 <= len(traces) <= 100,000"-> " or "<- "funcName contains only letters, digits, or underscores1000Can you implement the multi-threaded version by reusing your existing single-thread function rather than rewriting logic?
What if multiple call paths share the same highest frequency? Which one should be returned?
Now the input logs come from multiple threads and are interleaved. Each log entry includes a thread_id. Return the most frequent call path for each thread separately.
| Approach | Notes |
|---|---|
| Direct counter without stack tracking | Some candidates suggested just counting function names directly without a stack, but this misses the requirement that the 'call path' includes the full nested chain of callers, not just the individual function. Only works for a simplified variant that counts individual function invocations. |
Common mistakes: Spending too long on part 1 left no time for parts 2 and 3. One candidate only finished part 1 and was rejected.; Small parsing bugs (e.g. mishandling the '-> ' or '<- ' prefix, or how the path string is built) caused test case failures; the interviewer sometimes pointed these out.; One candidate completed all three parts and passed all tests but still received a rejection two days later, suggesting correctness alone is not sufficient.
Interviewer hints: The interviewer explicitly told at least one candidate to reuse the existing single-thread function for part 3 rather than rewrite the logic, and noted this was different from what previous candidates had done.; The interviewer pointed out small bugs (e.g. minor parsing issues) during the session; this happened even in a senior-level interview, though the candidate was unsure whether it counted against them.; Multiple reports relay that candidates were told (or learned from experience) to move fast on parts 1 and 2 because time is tight.
What passers do: Candidates who passed moved through all three parts quickly, finishing parts 1 and 2 before time pressure hit part 3. Multiple reports explicitly say speed on the first two parts is critical.; Passing candidates used a stack to track the current call path and a frequency counter (dict/map) keyed by the joined path string, updating on every entry event only.; For the multi-thread part, a passing candidate directly added a dict keyed by thread_id containing per-thread stack and count state, without rewriting the core logic.; One candidate was prompted by the interviewer to reuse the single-thread function for part 3 rather than rewriting; having a cleanly separated function from earlier made this possible.; All test cases must pass and be submitted before moving to the next part; the platform runs tests after each part.
Send them this page. It is free to read, no account needed.
What you just read — canonical solution, follow-up arc, what passing candidates actually did — exists for all 34 Roblox questions, refreshed monthly from new candidate reports.