Find the First Working Version
Company: OpenAI
Role: Machine Learning Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Onsite
Quick Answer: This question evaluates understanding of search algorithms, version normalization, and monotonic predicates, along with competency in parsing and ordering semantic version components.
Part 1: Earliest Working Version in a Flat Sorted List
Constraints
- 0 <= len(versions) <= 200000
- Each version string has 1 to 3 dot-separated non-negative integers
- Each numeric component is in the range [0, 10^9]
- Missing minor or patch components are treated as 0 for comparison
- The input list is sorted by normalized `(major, minor, patch)` order
Examples
Input: (['1', '1.2', '1.2.5', '2'], '1.2')
Expected Output: '1.2'
Explanation: `1.2` is the first version in the sorted list that is at least `1.2.0`.
Input: (['1', '1.0.1', '1.1', '2'], '1.0.2')
Expected Output: '1.1'
Explanation: `1.0.1` is too early, and `1.1` is the first normalized version greater than or equal to `1.0.2`.
Input: (['12', '12.0.1', '12.3'], '12.0.0')
Expected Output: '12'
Explanation: `12` normalizes to `12.0.0`, so it already works and is the earliest answer.
Input: ([], '1')
Expected Output: None
Explanation: An empty list has no working version.
Input: (['0.9', '1', '1.5'], '2')
Expected Output: None
Explanation: All listed versions are earlier than `2.0.0`.
Hints
- Convert each version string into a fixed-length 3-integer tuple before comparing.
- Because the predicate changes from `False` to `True` only once, think about binary search instead of scanning the whole list.
Part 2: Earliest Working Version in a Grouped Version Catalog
Constraints
- 0 <= len(catalog) <= 100000
- Major groups are sorted by unique major number
- Within each major group, minor groups are sorted by unique minor number and are non-empty
- Within each `(major, minor)` group, patches are sorted, unique, and non-empty
- All major, minor, and patch values are integers in the range [0, 10^9]
- The `works_from` string has 1 to 3 dot-separated numeric components; missing components count as 0
Examples
Input: ([(1, [(0, [0, 2]), (2, [1])]), (2, [(0, [0]), (1, [0, 3])])], '1.2.0')
Expected Output: '1.2.1'
Explanation: In major `1` and minor `2`, the first patch at least `0` is `1`.
Input: ([(1, [(0, [0, 5]), (2, [0])]), (2, [(0, [0])])], '1.0.6')
Expected Output: '1.2.0'
Explanation: There is no patch in `1.0.*` that is at least `6`, so the answer moves to the next available minor in the same major.
Input: ([(1, [(0, [0, 1]), (1, [0])]), (3, [(0, [0])])], '2')
Expected Output: '3.0.0'
Explanation: `2` means `2.0.0`, and the first existing version at or after that is `3.0.0`.
Input: ([], '1.0.0')
Expected Output: None
Explanation: An empty catalog contains no versions.
Input: ([(1, [(0, [0])]), (2, [(1, [5])])], '3')
Expected Output: None
Explanation: The target `3.0.0` is later than every existing version in the catalog.
Hints
- Think of the search as finding the first existing `(major, minor, patch)` that is lexicographically at least the target tuple.
- You can binary search at one level, then continue inside the matching group instead of materializing every full version.