Most-Read Page Across Every Valid Storyline
Reported by candidates from Duolingo's online assessment. Pattern, common pitfall, and the honest play if you blank under the timer.
Duolingo reported this one in October 2022, and the setup is odd: a 50-page branching story where every storyline starts on page 1 and you total up page visits across all valid paths that hit an ending. It looks like a graph walk, but the twist is that each option of a choice page can be used once per storyline. That makes it a DFS with state, and with at most 12 choice pages the state is small. If you blank on how to track used options, StealthCoder is the safety net running invisibly during the live OA.
The problem
You are reading a branching story book whose pages are numbered from 1 through 50. Every storyline begins on page 1. On an ordinary page, reading continues to the next numbered page. A row [page, first, second] in choices makes page a choice page whose two options jump to first and second. A page in endings finishes the current storyline immediately. Within one storyline, each option of a choice page can be used at most once. Revisiting a choice page after both options have already been used makes that branch invalid. Advancing past page 50 also makes a branch invalid. Consider every valid storyline that reaches an ending. Count every visit to every page across all of those storylines. Return [page, count] for the page with the greatest total count. If several pages tie, return the smallest page number. If no valid storyline reaches an ending, return [-1]. Function mostReadStoryPage(endings: int[], choices: int[][]) → long[] Examples Example 1 endings = [5,10] choices = [[3,7,9],[9,10,8]] return = [9,6] There are four valid ending-reaching storylines. Pages 1, 2, 3, 8, and 10 are each read four times, while page 9 is read six times. Therefore the result is [9, 6]. Example 2 endings = [5] choices = [[1,1,1]] return = [-1] Both options on page 1 return to page 1. After each option has been used once, the branch is invalid and never reaches page 5. No valid storyline exists. Constraints 1 <= endings.length and every ending is a unique page from 1 through 50. 0 <= choices.length <= 12. Every choice row has exactly three page numbers from 1 through 50. Choice-page numbers are unique. Counts fit in a signed 64-bit integer.
Reported by candidates. Source: FastPrep
Pattern and pitfall
The trick is that state is just which of the 24 options (12 choice pages, 2 each) have been used, so a bitmask. Run a DFS from page 1 carrying that mask. On an ordinary page, move to page+1, and past 50 the branch dies. On a choice page, try each unused option, set its bit, and recurse. If both are used, the branch is invalid. Hitting an ending stops the storyline. Plain enumeration works if paths are bounded, since each option is used once. The pitfall is counting visits only for valid storylines. A path that dies shouldn't contribute anything. So return (number of valid endings, visit counts) per subtree, or memoize on (page, mask) and multiply counts. Also watch Example 2, where both options loop back to page 1 and nothing is valid. Break ties by smallest page, and use 64-bit counts. If the memoized combination feels slippery live, StealthCoder is the hedge.
The honest play: practice the pattern, and have StealthCoder ready for the one you didn't see coming.
You can drill Most-Read Page Across Every Valid Storyline 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 for the candidate who saw this exact problem leak two days before his OA and wondered if anyone had a play.
Get StealthCoderRelated leaked OAs
You've seen the question.
Make sure you actually pass Duolingo's OA.
Duolingo reuses patterns across OAs. Built for the candidate who saw this exact problem leak two days before his OA and wondered if anyone had a play. Works on HackerRank, CodeSignal, CoderPad, and Karat.
Most-Read Page Across Every Valid Storyline FAQ
What's the core trick in the Duolingo story page problem?+
Treat it as a DFS over (page, mask of used choice options). With at most 12 choice pages, the mask has 24 bits max, and each option can only be used once per storyline. That bounds the search and makes the visit counting well defined.
How do I count visits only for valid storylines?+
Don't add visits as you go. Have each DFS call return how many valid ending-reaching paths exist below it and the visit totals for those paths only. Dead branches return zero. Then add the current page's visit times the number of valid completions.
How hard is this really?+
Medium to hard. The ideas are simple, DFS and a bitmask, but the validity rule and visit accounting trip people up. Expect to spend most of your time on the return values from the recursion, not the traversal itself.
What edge cases should I test?+
Example 2, where both options loop to the same page and nothing is valid, so you return [-1]. Also test a path running past page 50, a choice page that is also an ending, and ties in total count where the smallest page must win.
How do I prepare in 48 hours?+
Write a DFS with a bitmask for used edges on a small graph, then add memoization on (page, mask) returning counts. Practice returning tuples from recursion. Then run Example 1 by hand and confirm page 9 gives 6 before submitting.