Implement Journey Counter and Speeder Detector
Company: Headway
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: hard
Interview Round: Technical Screen
You are given a highway toll log parser with two classes: `LogEntry` and `LogFile`.
Each log line has the format:
`timestamp license_plate location booth_type`
Example:
`44776.619 KTB918 310E MAINROAD`
Where:
- `timestamp` is a floating-point number in seconds
- `license_plate` is a string such as `KTB918`
- `location` is an integer followed by a direction letter, such as `310E` or `400W`
- `booth_type` is one of `ENTRY`, `MAINROAD`, or `EXIT`
A journey is defined as:
- one `ENTRY`
- followed by zero or more `MAINROAD` records
- followed by one `EXIT`
Assume the log contains only complete journeys and no missing records.
Implement or fix the following:
1. **Parse log entries correctly**
- In `LogEntry.__init__(log_line)`, parse the timestamp as a numeric value, not a string.
- Parse the location into:
- `location`: integer part
- `direction`: `EAST` if the suffix is `E`, `WEST` if the suffix is `W`
2. **Implement `count_journeys()`**
- Return the total number of complete journeys in the log.
3. **Implement `catch_speeders()`**
- Return a collection of license plates for journeys that violate the speed rules.
- Each pair of consecutive checkpoints within the same journey represents a `10 km` segment.
- Segment speed is:
`speed_kmh = (10 * 3600) / time_diff_seconds`
A journey is considered speeding if either of the following is true:
- any single `10 km` segment has speed at least `130 km/h`, or
- at least two `10 km` segments in the same journey have speed at least `120 km/h`
Additional rules:
- A plate may appear multiple times in the result if it has multiple speeding journeys.
- Count each speeding journey only once, even if it contains more than two speeding segments.
- Do not compute speeds across different journeys for the same car; in particular, do not connect an `EXIT` from one journey to a later `ENTRY`.
You may assume the input order is the log order.
Quick Answer: This question evaluates parsing, time-series processing, and algorithmic reasoning for detecting complete journeys and speed violations from structured toll log entries, covering numeric timestamp conversion, location and direction extraction, stateful journey grouping, and segment-based speed calculations.
You are given a highway toll log. Each log line has the format `timestamp license_plate location booth_type`, for example `44776.619 KTB918 310E MAINROAD`, where:
- `timestamp` is a floating-point number of seconds.
- `license_plate` is a string such as `KTB918`.
- `location` is an integer followed by a direction letter, such as `310E` (EAST) or `400W` (WEST).
- `booth_type` is one of `ENTRY`, `MAINROAD`, or `EXIT`.
A journey is one `ENTRY`, then zero or more `MAINROAD` records, then one `EXIT`. The log contains only complete journeys with no missing records, and is given in log order.
Implement a function `solution(log_contents)` that parses the log (timestamps as numbers, locations split into integer + EAST/WEST direction) and returns `[journey_count, speeders]` where:
1. `journey_count` is the total number of complete journeys (equal to the number of `ENTRY` records).
2. `speeders` is the list of license plates for journeys that violate the speed rules, in log order (by EXIT).
Each pair of consecutive checkpoints within the same journey is a 10 km segment, and its speed is `speed_kmh = (10 * 3600) / time_diff_seconds`. A journey is speeding if any single segment has speed at least 130 km/h, OR at least two segments have speed at least 120 km/h. Count each speeding journey only once even if it has more than two fast segments, and never compute speed across different journeys (do not connect an EXIT to a later ENTRY). A plate may appear multiple times in `speeders` if it has multiple speeding journeys.
Constraints
- The log contains only complete journeys (every ENTRY has a matching later EXIT) with no missing records.
- Lines are given in log (chronological) order.
- booth_type is always one of ENTRY, MAINROAD, or EXIT.
- The direction suffix is always 'E' or 'W'.
- time_diff between consecutive checkpoints in a journey is strictly positive.
- A license plate may have multiple journeys; treat each independently.
Examples
Input: ('0.000 JOX304 250E ENTRY\n400.000 JOX304 260E MAINROAD\n100.000 THX138 110E ENTRY\n900.000 JOX304 270E MAINROAD\n600.000 THX138 120E MAINROAD\n1400.000 JOX304 280E EXIT\n1200.000 THX138 290E EXIT',)
Expected Output: [2, []]
Explanation: Two interleaved journeys. JOX304's segments (90, 72, 72 km/h) and THX138's segments (72, 60 km/h) are all well below the thresholds, so 2 journeys and no speeders.
Input: ('1000.000 TST002 270W ENTRY\n1275.000 TST002 260W EXIT\n2000.000 TST003 270W ENTRY\n2300.000 TST003 260W MAINROAD\n2600.000 TST003 250W EXIT\n5000.000 TST003 270W ENTRY\n5300.000 TST003 260W MAINROAD\n5600.000 TST003 250W EXIT',)
Expected Output: [3, ['TST002', 'TST003', 'TST003']]
Explanation: TST002's single segment is 36000/275 = 130.9 km/h (>= 130 -> speeder). Each TST003 journey has two segments at exactly 120.0 km/h (two segments >= 120 -> speeder), and it speeds in both of its journeys, so it appears twice.
Input: ('',)
Expected Output: [0, []]
Explanation: Empty log: no journeys and no speeders.
Input: ('100.000 SOLO1 500E ENTRY\n376.000 SOLO1 510E EXIT',)
Expected Output: [1, ['SOLO1']]
Explanation: A single ENTRY->EXIT journey with time_diff 276s gives 36000/276 = 130.4 km/h (>= 130), so SOLO1 is a speeder.
Input: ('0.000 SLOW1 100E ENTRY\n400.000 SLOW1 110E MAINROAD\n800.000 SLOW1 120E EXIT',)
Expected Output: [1, []]
Explanation: Both segments are 36000/400 = 90 km/h, below all thresholds, so no speeder.
Input: ('0.000 MIXED 100E ENTRY\n300.000 MIXED 110E MAINROAD\n700.000 MIXED 120E EXIT',)
Expected Output: [1, []]
Explanation: First segment is 120.0 km/h (one fast segment) and the second is 90 km/h. Only one segment >= 120 and none >= 130, so the journey is not a speeder.
Hints
- Parse the timestamp with float(), not as a string — otherwise the speed subtraction is meaningless and no speeder is ever detected.
- The journey count is simply the number of ENTRY records, since every journey begins with exactly one ENTRY.
- Track an in-progress journey per plate: start the timestamp list on ENTRY, append on MAINROAD, and finalize+check on EXIT. Never carry timestamps across an EXIT into a later ENTRY.
- Speed for a 10 km segment is 36000 / time_diff. Flag the journey if any segment is >= 130, or if two or more segments are >= 120; stop checking that journey once it is flagged.