Quick Overview

Given vehicle maintenance events of the form open or end, time and type, decide whether the vehicle is ready to go: every opened task must be ended and no type may have two tasks open at once. It tests event ordering with ties, per-type state tracking, and careful validation of unmatched events.

Decide Whether a Vehicle Is Ready from Maintenance Open and End Events

Company: Waymo

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Technical Screen

A vehicle's maintenance log is a list of events. Each event either opens or ends a maintenance task of one type, such as `oil` or `brake`. The vehicle is ready to go only if every task that was opened has also been ended, and no type ever had two tasks open at the same time. Write a function that decides whether the vehicle is ready to go. ### Function Signature ```python def is_ready_to_go(events: list[list[str]]) -> bool: ``` ### Rules - `events[i] = [action, time, type]`, where `action` is `"open"` or `"end"`, `time` is a non-negative integer written as a decimal string, and `type` names the maintenance task. - The events are not necessarily listed in time order. Process them in increasing order of `time`, comparing times as integers (`"9"` comes before `"10"`). Events with equal times are processed in the order in which they appear in `events`. - Processing `"open"` for a type that already has an open task is a violation: each type can have at most one open task at a time. Different types can be open at the same time. - Processing `"end"` for a type closes that type's open task. If the type has no open task, it is a violation. - Return `True` if no violation occurs and no task is still open after the last event; otherwise return `False`. An empty list returns `True`. - Because of the tie rule, `[["open", "4", "oil"], ["end", "4", "oil"]]` returns `True`, while `[["end", "4", "oil"], ["open", "4", "oil"]]` returns `False`. ### Constraints - `0 <= len(events) <= 10^5` - `action` is exactly `"open"` or `"end"`. - `time` is the decimal form of an integer `0 <= time <= 10^9`, with no leading zeros (`"0"` itself is allowed), so every parsed time fits in a 32-bit signed integer. - `type` has 1 to 20 characters, each a lowercase English letter or an underscore. Types are compared exactly. ### Examples **Example 1** ```text Input: events = [["end", "6", "brake"], ["open", "1", "oil"], ["open", "2", "brake"], ["end", "4", "oil"], ["open", "4", "oil"], ["end", "9", "oil"]] Output: True ``` In time order: `oil` opens at 1, `brake` opens at 2, `oil` ends at 4 and opens again at 4 (the end comes first in the input), `brake` ends at 6, and `oil` ends at 9. No type overlaps itself and nothing is left open. **Example 2** ```text Input: events = [["open", "1", "oil"], ["open", "3", "oil"], ["end", "5", "oil"], ["end", "7", "oil"]] Output: False ``` At time 3 an `oil` task opens while the one opened at time 1 is still open. **Example 3** ```text Input: events = [["open", "2", "tire"], ["end", "8", "tire"], ["open", "5", "brake"]] Output: False ``` The `tire` task is opened and ended, but the `brake` task opened at time 5 is never ended.

Overview: Given vehicle maintenance events of the form open or end, time and type, decide whether the vehicle is ready to go: every opened task must be ended and no type may have two tasks open at once. It tests event ordering with ties, per-type state tracking, and careful validation of unmatched events.

Read the full Waymo Software Engineer interview experience this question came from

A vehicle's maintenance log is a list of events. Each event either opens or ends a maintenance task of one type, such as `oil` or `brake`. The vehicle is ready to go only if every task that was opened has also been ended, and no type ever had two tasks open at the same time. Implement `is_ready_to_go(events)`, which returns whether the vehicle is ready to go (`True`/`False` in Python, `true`/`false` in JavaScript, Java and C++). ### Rules - `events[i] = [action, time, type]`, where `action` is `"open"` or `"end"`, `time` is a non-negative integer written as a decimal string, and `type` names the maintenance task. - The events are not necessarily listed in time order. Process them in increasing order of `time`, comparing times as integers (`"9"` comes before `"10"`). Events with equal times are processed in the order in which they appear in `events`. - Processing `"open"` for a type that already has an open task is a violation: each type can have at most one open task at a time. Different types can be open at the same time. - Processing `"end"` for a type closes that type's open task. If the type has no open task, it is a violation. - Return `True` if no violation occurs and no task is still open after the last event; otherwise return `False`. An empty list returns `True`. - Because of the tie rule, `[["open", "4", "oil"], ["end", "4", "oil"]]` returns `True`, while `[["end", "4", "oil"], ["open", "4", "oil"]]` returns `False`. ### Constraints - `0 <= len(events) <= 10^5` - Each `events[i]` is a list of exactly three strings `[action, time, type]`. - `action` is exactly `"open"` or `"end"`. - `time` is the decimal form of an integer `0 <= time <= 10^9`, with no leading zeros (`"0"` itself is allowed), so every parsed time fits in a 32-bit signed integer; no value in this problem exceeds 2^31 - 1. - `type` has 1 to 20 characters, each a lowercase English letter or an underscore. Types are compared exactly. ### Example 1 ```text Input: events = [["end", "6", "brake"], ["open", "1", "oil"], ["open", "2", "brake"], ["end", "4", "oil"], ["open", "4", "oil"], ["end", "9", "oil"]] Output: True ``` In time order: `oil` opens at 1, `brake` opens at 2, `oil` ends at 4 and opens again at 4 (the end comes first in the input), `brake` ends at 6, and `oil` ends at 9. No type overlaps itself and nothing is left open. ### Example 2 ```text Input: events = [["open", "1", "oil"], ["open", "3", "oil"], ["end", "5", "oil"], ["end", "7", "oil"]] Output: False ``` At time 3 an `oil` task opens while the one opened at time 1 is still open.

Constraints

  • 0 <= len(events) <= 10^5
  • Each events[i] is a list of exactly three strings [action, time, type].
  • action is exactly "open" or "end".
  • time is the decimal form of an integer 0 <= time <= 10^9, with no leading zeros ("0" itself is allowed), so every parsed time fits in a 32-bit signed integer.
  • type has 1 to 20 characters, each a lowercase English letter or an underscore. Types are compared exactly.

Examples

Input: ([],)

Expected Output: True

Explanation: Empty log: no violation and nothing is open, so the vehicle is ready.

Input: ([['open', '5', 'oil']],)

Expected Output: False

Explanation: Single open event: the oil task is still open after the last event.

Hints

  1. The order of the input list is not the order in which events take effect. Work out exactly which order the rules require, including what happens when two events share a time.
  2. Times are given as strings, but "9" must come before "10": compare their integer values, not their text.
  3. Each type can have at most one open task, so at any moment a type is either open or not open.

Loading coding console...

Show the approach

Approach

Algorithm: sort the event indices by the key (integer value of time, original index), then scan the events in that order while keeping a set of the types that currently have an open task. Parsing time as an integer gives the required numeric order ("9" before "10", "20" before "100"), and using the original index as the secondary key (equivalently, a stable sort on time) keeps equal-time events in input order, exactly as the tie rule requires.

Invariant: after processing a prefix of the ordered events with no violation, the set contains exactly the types whose task is currently open. An "open" for a type already in the set is a violation; otherwise the type is added. An "end" for a type not in the set is a violation; otherwise the type is removed. A violation makes the answer False immediately, because no later event can undo it. If the scan finishes without a violation, the answer is True exactly when the set is empty, meaning no task is still open after the last event.

Edge cases: an empty list returns True; a single open or a single end returns False; types are matched exactly, so "oil" and "oil_" are independent; different types may be open at the same time; an end that comes before the same type's open in time order is a violation even if that type is opened later; comparing time strings lexicographically, processing events in input order, breaking equal-time ties by action, or using one global open counter instead of per-type state all give wrong answers. Times are at most 10^9, so they fit in a 32-bit signed integer.

Time complexity:
O(n log n)
Space complexity:
O(n)