> ## Documentation Index
> Fetch the complete documentation index at: https://leetcode-py.wisl.dev/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> leetcode-py is a Python LeetCode practice environment generator with one CLI: lcpy. It is not a service or platform.
> Each problem is a directory under leetcode/ with README.md, solution.py, test_solution.py, helpers.py, and playground.ipynb. lcpy gen creates them from JSON templates bundled with the package.
> Examples are backed by tests; copy them verbatim.

# Design Underground System Python Solution

> Tested Python solution for LeetCode 1396 with 13 pytest cases. Generate a practice environment with lcpy.

LeetCode 1396, [Medium](/catalog/medium). Topics: [Hash Table](/catalog/topics/hash-table), [String](/catalog/topics/string), [Design](/catalog/topics/design). [View on LeetCode](https://leetcode.com/problems/design-underground-system/description/).

Generate this problem as a practice environment: tested reference solution, 13 [parametrized pytest cases](/practice/testing), and a playground notebook:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
lcpy gen -n 1396   # by problem number
lcpy gen -s design_underground_system   # by problem name
```

## Problem

An underground railway system is keeping track of customer travel times between different stations. They are using this data to calculate the average time it takes to travel from one station to another.

Implement the `UndergroundSystem` class:

* `UndergroundSystem()` Initializes the object of the underground system.
* `void checkIn(int id, string stationName, int t)` A customer with a card ID equal to `id`, checks in at the station `stationName` at time `t`. A customer can only be checked into one place at a time.
* `void checkOut(int id, string stationName, int t)` A customer with a card ID equal to `id`, checks out from the station `stationName` at time `t`.
* `double getAverageTime(string startStation, string endStation)` Returns the average time it takes to travel from `startStation` to `endStation`. The average time is computed from all the previous traveling times from `startStation` to `endStation` that happened **directly**, meaning a check in at `startStation` followed by a check out from `endStation`. The time it takes to travel from `startStation` to `endStation` **may be different** from the time it takes to travel from `endStation` to `startStation`. There will be at least one customer that has traveled from `startStation` to `endStation` before `getAverageTime` is called.

You may assume all calls to the `checkIn` and `checkOut` methods are consistent. If a customer checks in at time `t1` then checks out at time `t2`, then `t1 < t2`. All events happen in chronological order.

### Examples

```
Input
["UndergroundSystem","checkIn","checkIn","checkIn","checkOut","checkOut","checkOut","getAverageTime","getAverageTime","checkIn","getAverageTime","checkOut","getAverageTime"]
[[],[45,"Leyton",3],[32,"Paradise",8],[27,"Leyton",10],[45,"Waterloo",15],[27,"Waterloo",20],[32,"Cambridge",22],["Paradise","Cambridge"],["Leyton","Waterloo"],[10,"Leyton",24],["Leyton","Waterloo"],[10,"Waterloo",38],["Leyton","Waterloo"]]
Output
[null,null,null,null,null,null,null,14.00000,11.00000,null,11.00000,null,12.00000]
```

**Explanation:**

```
undergroundSystem.checkIn(45, "Leyton", 3);
undergroundSystem.checkIn(32, "Paradise", 8);
undergroundSystem.checkIn(27, "Leyton", 10);
undergroundSystem.checkOut(45, "Waterloo", 15); // 15-3 = 12
undergroundSystem.checkOut(27, "Waterloo", 20); // 20-10 = 10
undergroundSystem.checkOut(32, "Cambridge", 22); // 22-8 = 14
undergroundSystem.getAverageTime("Paradise", "Cambridge"); // return 14.00000, (14) / 1 = 14
undergroundSystem.getAverageTime("Leyton", "Waterloo"); // return 11.00000, (10 + 12) / 2 = 11
undergroundSystem.checkIn(10, "Leyton", 24);
undergroundSystem.getAverageTime("Leyton", "Waterloo"); // return 11.00000
undergroundSystem.checkOut(10, "Waterloo", 38); // 38-24 = 14
undergroundSystem.getAverageTime("Leyton", "Waterloo"); // return 12.00000, (10 + 12 + 14) / 3 = 12
```

```
Input
["UndergroundSystem","checkIn","checkOut","getAverageTime","checkIn","checkOut","getAverageTime","checkIn","checkOut","getAverageTime"]
[[],[10,"Leyton",3],[10,"Paradise",8],["Leyton","Paradise"],[5,"Leyton",10],[5,"Paradise",16],["Leyton","Paradise"],[2,"Leyton",21],[2,"Paradise",30],["Leyton","Paradise"]]
Output
[null,null,null,5.00000,null,null,5.50000,null,null,6.66667]
```

**Explanation:**

```
undergroundSystem.checkIn(10, "Leyton", 3);
undergroundSystem.checkOut(10, "Paradise", 8); // 8-3 = 5
undergroundSystem.getAverageTime("Leyton", "Paradise"); // return 5.00000, (5) / 1 = 5
undergroundSystem.checkIn(5, "Leyton", 10);
undergroundSystem.checkOut(5, "Paradise", 16); // 16-10 = 6
undergroundSystem.getAverageTime("Leyton", "Paradise"); // return 5.50000, (5 + 6) / 2 = 5.5
undergroundSystem.checkIn(2, "Leyton", 21);
undergroundSystem.checkOut(2, "Paradise", 30); // 30-21 = 9
undergroundSystem.getAverageTime("Leyton", "Paradise"); // return 6.66667, (5 + 6 + 9) / 3 = 6.66667
```

### Constraints

* `1 <= id, t <= 10^6`
* `1 <= stationName.length, startStation.length, endStation.length <= 10`
* All strings consist of uppercase and lowercase English letters and digits.
* There will be at most `2 * 10^4` calls in total to `checkIn`, `checkOut`, and `getAverageTime`.
* Answers within `10^-5` of the actual value will be accepted.

## Solution

Reference implementation from [solution.py on GitHub](https://github.com/wislertt/leetcode-py/blob/main/leetcode/design_underground_system/solution.py), full suite in [test\_solution.py](https://github.com/wislertt/leetcode-py/blob/main/leetcode/design_underground_system/test_solution.py):

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
class UndergroundSystem:
    # Time: check_in O(1), check_out O(1), get_average_time O(1)
    # Space: O(P + R) for P passengers in transit and R distinct routes
    def __init__(self) -> None:
        self.checked_in: dict[int, tuple[str, int]] = {}
        self.trips: dict[tuple[str, str], tuple[int, int]] = {}

    def check_in(self, id: int, station_name: str, t: int) -> None:
        self.checked_in[id] = (station_name, t)

    def check_out(self, id: int, station_name: str, t: int) -> None:
        start_station, start_t = self.checked_in.pop(id)
        route = (start_station, station_name)
        total, count = self.trips.get(route, (0, 0))
        self.trips[route] = (total + t - start_t, count + 1)

    def get_average_time(self, start_station: str, end_station: str) -> float:
        total, count = self.trips[(start_station, end_station)]
        return total / count
```

## Complexity

| Time | Space |
| - | - |
| check\_in O(1), check\_out O(1), get\_average\_time O(1) | O(P + R) for P passengers in transit and R distinct routes |

## Tags

[NeetCode All](/catalog/neetcode).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.