Consolidate On-Call Rotation Segments
GoogleSoftware Engineer · Online AssessmentLast updated
Consolidate overlapping on-call shift intervals into non-overlapping segments, each annotated with the sorted list of active engineers.
Problem Statement
You are building a dashboard to visualize on-call coverage for an engineering team. The input is a list of on-call shifts, where each shift is defined by a start time, an end time, and an engineer's identifier.
Because multiple engineers can be on-call at the same time, shifts can overlap. You need to segment the overall timeline into contiguous, disjoint intervals [start, end) such that during each interval, the exact set of active engineers remains constant.
Write a function to consolidate these shifts into a sorted list of non-overlapping intervals, each accompanied by the lexicographically sorted list of active engineers.
Requirements
- Each output interval
(start, end, engineers)represents a half-open interval[start, end)withstart < end. engineersmust be sorted in ascending lexicographical order and contain no duplicate IDs.- Periods with no active engineers (gaps) must be omitted from the output.
- Adjacent intervals with the exact same set of active engineers must be merged into a single continuous interval.
- Output intervals must be sorted by
starttime in ascending order.
Interface
def consolidate_shifts(
shifts: list[tuple[int, int, str]]
) -> list[tuple[int, int, list[str]]]:
pass
Input & Output Format
- Input:
shiftsis a list of tuples(start, end, engineer_id), wherestartandendare integers, andengineer_idis a string. - Output: A list of tuples
(start, end, engineers), wherestartandendare integers, andengineersis alist[str].
Examples
Example 1
shifts = [
(1, 5, "Alice"),
(3, 7, "Bob")
]
# Output:
# [
# (1, 3, ["Alice"]),
# (3, 5, ["Alice", "Bob"]),
# (5, 7, ["Bob"])
# ]
Explanation: From 1 to 3, only Alice is on duty. At time 3, Bob begins his shift, so both Alice and Bob are active until Alice's shift ends at 5. Bob continues alone until time 7.
Example 2
shifts = [
(1, 4, "Alice"),
(2, 4, "Bob"),
(6, 8, "Alice")
]
# Output:
# [
# (1, 2, ["Alice"]),
# (2, 4, ["Alice", "Bob"]),
# (6, 8, ["Alice"])
# ]
Explanation: Between 4 and 6, no engineer is on-call, so that interval is omitted.
Constraints
0 <= len(shifts) <= 2 * 10^40 <= start < end <= 10^91 <= len(engineer_id) <= 30, containing only alphanumeric characters.
Hints
Hint 1
Deconstruct each shift into two distinct events: a start event and an end event at their respective timestamps.
Hint 2
Collect all unique timestamps in sorted order, or sort event points such that all departures and arrivals at the same timestamp are processed before recording the next interval.
Hint 3
Maintain the set of active engineers as you sweep across the timeline, and remember to merge consecutive intervals if they have the exact same active engineer set.
Follow-up questions
- How would you optimize the solution if you needed to support streaming updates (e.g., adding or canceling shifts dynamically)?
- How would you adapt your data structure to support point queries, such as retrieving all active engineers at a specific timestamp t in O(log n) time?
- If an engineer can be assigned multiple overlapping shifts with different role weights, how would you report the cumulative weight alongside the active list?
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.