Find list pair with maximum overlap
Company: Pinterest
Role: Data Scientist
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Onsite
Quick Answer: This question evaluates proficiency with set operations and similarity metrics (Jaccard), scalable algorithm design and complexity analysis for large datasets, and the ability to handle streaming updates and deterministic tie-breaking.
Part 1: Inverted-Index Overlap Statistics
Constraints
- 1 <= number of lists <= 200000, but your algorithm should also handle empty input.
- The total number of raw items across all iterables can be up to 5000000.
- Items are case-sensitive strings.
- Duplicates within the same list must be ignored.
Examples
Input: ({'L1': ['A', 'B', 'C'], 'L2': ['A', 'C', 'D'], 'L3': ['B', 'C', 'E'], 'L4': []},)
Expected Output: {'membership_count': 9, 'unique_item_count': 5, 'pair_updates': 5, 'pair_counts': [('L1', 'L2', 2), ('L1', 'L3', 2), ('L2', 'L3', 1)]}
Explanation: After deduplication there are 9 distinct memberships. Item frequencies are A:2, B:2, C:3, D:1, E:1, so pair_updates = 1 + 1 + 3 = 5.
Input: ({'A': ['x', 'x'], 'B': ['x', 'y', 'y'], 'C': []},)
Expected Output: {'membership_count': 3, 'unique_item_count': 2, 'pair_updates': 1, 'pair_counts': [('A', 'B', 1)]}
Explanation: Duplicates inside a list do not count. Only item x is shared.
Hints
- First deduplicate each individual list, then think item -> lists that contain it.
- If one item appears in f different lists, it contributes C(f, 2) pair updates.
Part 2: Maximum-Overlap List Pair
Constraints
- 1 <= number of lists <= 200000, but your function should also handle 0 or 1 list.
- The total number of raw items across all iterables can be up to 5000000.
- Items are case-sensitive strings.
- An O(N^2) all-pairs comparison is not acceptable for large inputs.
Examples
Input: ({'L1': ['A', 'B', 'C'], 'L2': ['A', 'C', 'D'], 'L3': ['B', 'C', 'E'], 'L4': []},)
Expected Output: ('L1', 'L2', 2, 0.5)
Explanation: L1-L2 and L1-L3 both have overlap 2 and Jaccard 0.5, so lexicographic order picks ('L1', 'L2').
Input: ({'A': ['x', 'y'], 'B': ['x'], 'C': ['y', 'z']},)
Expected Output: ('A', 'B', 1, 0.5)
Explanation: A-B and A-C both overlap by 1, but A-B has the larger Jaccard score.
Hints
- Use an inverted index item -> list names to count only pairs that actually share at least one item.
- If no pair shares an item, handle the zero-overlap case separately.
Part 3: Top-K Overlapping List Pairs
Constraints
- 1 <= number of lists <= 200000, but your solution should also handle empty input.
- The total number of raw items across all iterables can be up to 5000000.
- Items are case-sensitive strings.
- Only pairs with overlap_count > 0 should appear in the result.
Examples
Input: ({'L1': ['A', 'B', 'C'], 'L2': ['A', 'C', 'D'], 'L3': ['B', 'C', 'E'], 'L4': []}, 2)
Expected Output: [('L1', 'L2', 2, 0.5), ('L1', 'L3', 2, 0.5)]
Explanation: The two best positive-overlap pairs both have overlap 2 and Jaccard 0.5.
Input: ({'A': ['x', 'x', 'y'], 'B': ['x'], 'C': ['x', 'y'], 'D': ['z']}, 3)
Expected Output: [('A', 'C', 2, 1.0), ('A', 'B', 1, 0.5), ('B', 'C', 1, 0.5)]
Explanation: Duplicates are ignored. A-C is the only pair with overlap 2.
Hints
- First compute positive-overlap pair counts using an inverted index.
- A heap of size k can avoid fully sorting every candidate pair.
Part 4: Best Pair Under Streaming List Updates
Constraints
- 1 <= number of updates <= 300000, but your solution should also handle empty input.
- Each update is a pair (list_name, item).
- A duplicate update for an already-present (list_name, item) must not change the state.
- A fully recomputed all-pairs scan after every update is too slow.
Examples
Input: ([('L1', 'A'), ('L2', 'B'), ('L2', 'B'), ('L1', 'B'), ('L2', 'A')],)
Expected Output: [None, ('L1', 'L2', 0, 0.0), ('L1', 'L2', 0, 0.0), ('L1', 'L2', 1, 0.5), ('L1', 'L2', 2, 1.0)]
Explanation: The duplicate update ('L2', 'B') does nothing. The best pair evolves as overlap grows.
Input: ([('L1', 'A'), ('L2', 'A'), ('L3', 'B'), ('L4', 'B'), ('L1', 'X'), ('L2', 'X')],)
Expected Output: [None, ('L1', 'L2', 1, 1.0), ('L1', 'L2', 1, 1.0), ('L1', 'L2', 1, 1.0), ('L3', 'L4', 1, 1.0), ('L1', 'L2', 2, 1.0)]
Explanation: Adding a unique item to L1 lowers the Jaccard score of L1-L2, so L3-L4 temporarily becomes the best pair.
Hints
- An update only changes overlap counts for lists that already had the same item, and it only changes Jaccard values for positive-overlap neighbors of the updated list.
- Use lazy heap entries: push new scores when something changes, and discard stale entries when they reach the top.