Design Prefix-Aware Deduplication for Homepage Content Rows
NetflixSoftware Engineer · Technical ScreenLast updated
Implement prefix-aware content row deduplication where protected row prefixes cannot share items across rows, while suffixes and exempt rows only enforce row-internal uniqueness.
Problem Statement
On the Netflix homepage, content is arranged in horizontal rows (e.g., "Trending Now", "Top 10"). To maintain visual diversity above the fold, the platform enforces a prefix deduplication policy:
- Protected Prefix: For a non-exempt row
i, the firstprefix_limits[i]items added to the row must not appear in the protected prefix of any previously constructed non-exempt row. - Suffix / Beyond Prefix: Once a row's protected prefix reaches
prefix_limits[i]items, subsequent items added to that row only need to be unique within that specific row (they may duplicate items from other rows' prefixes or suffixes). - Within-Row Uniqueness: No row may contain the same title more than once, regardless of prefix or suffix position.
- Exempt Rows: Rows marked as exempt (e.g., "Continue Watching" or "My List") do not participate in cross-row prefix deduplication: they do not check against previously used prefix items, nor do their items block subsequent rows' prefixes.
- Shortfall: If a row's candidate list runs out of eligible titles before reaching its target length
target_lengths[i], output all valid titles gathered without error.
Implement the function deduplicate_homepage_rows that processes rows in order and returns the filtered rows.
Function Signature
def deduplicate_homepage_rows(
candidate_rows: list[list[str]],
prefix_limits: list[int],
target_lengths: list[int],
exempt_row_indices: set[int] | None = None,
) -> list[list[str]]:
pass
Input & Constraints
candidate_rows: A list ofRlists, where each list contains string identifiers representing candidate titles in priority order.0 <= R <= 1000.prefix_limits: A list ofRnon-negative integers (0 <= prefix_limits[i] <= target_lengths[i]).target_lengths: A list ofRnon-negative integers (0 <= target_lengths[i] <= 500).exempt_row_indices: An optional set of row indices0 <= idx < Rindicating exempt rows.- Total number of candidate strings across all rows
<= 200,000.
Examples
Example 1
candidate_rows = [
["A", "B", "C", "D"],
["B", "A", "E", "B", "C"],
]
prefix_limits = [2, 2]
target_lengths = [3, 3]
exempt_row_indices = set()
# Output:
# [
# ["A", "B", "C"],
# ["E", "B", "C"]
# ]
Explanation:
- Row 0: Takes
"A"and"B"as prefix items. Global prefix set is now{"A", "B"}. Suffix takes"C". Row 0 target (3) reached. - Row 1: Candidate
"B"is rejected from prefix because it is in global prefix set. Candidate"A"is also rejected. Candidate"E"fills prefix slot 1. Candidates run out of new prefix items? Wait, the next candidate is"B"(still rejected for prefix) and"C"(not in prefix set, fills prefix slot 2). Wait, in candidate list:["B", "A", "E", "B", "C"]:"B"rejected for prefix (in global prefix set)."A"rejected for prefix (in global prefix set)."E"accepted as prefix item 1 (global prefix set now{"A", "B", "E"}).- Next is
"B"(rejected for prefix). "C"accepted as prefix item 2 (global prefix set now{"A", "B", "E", "C"}).- Prefix filled (size 2). Next candidate for suffix: list exhausted. Result for Row 1 is
["E", "C"].
Example 2 (With Suffix Duplication & Exempt Row)
candidate_rows = [
["X", "Y", "Z"],
["X", "W", "V"],
]
prefix_limits = [1, 1]
target_lengths = [2, 2]
exempt_row_indices = {1}
# Output:
# [
# ["X", "Y"],
# ["X", "W"]
# ]
Explanation: Row 1 is exempt, so "X" can be used as its prefix title even though Row 0 already used "X" in its prefix.
Hints
Hint 1
Maintain a global set for titles already committed to the protected prefixes of non-exempt rows, and a per-row set for titles already in the current row.
Hint 2
For each title candidate, determine whether the current row is still filling its prefix or its suffix to apply the correct eligibility check.
Hint 3
If a candidate is rejected during prefix filling, do not discard it permanently for the suffix unless it is already present in the current row; however, check whether candidate order must be strictly preserved as you iterate.
Follow-up questions
- How would you support a fallback pool where candidates skipped during the prefix phase can be reconsidered once the row transitions to its suffix phase?
- If candidate lists are lazily loaded from an external recommendation service via paginated RPCs, how would you restructure the row construction to minimize network latency and memory usage?
- How would you handle priority inversion if a high-value promotional title must appear in a specific row even if it conflicts with an earlier row's prefix?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.