Leetcode.com problem 1396: Design Underground System

James S. Plank

Tue May 30 23:50:06 EDT 2023

In case Leetcode's servers are down

You are to implement the following class and methods:

class UndergroundSystem {
  public:
    UndergroundSystem();
    void checkIn(int id, string stationName, int t);
    void checkOut(int id, string stationName, int t);
    double getAverageTime(string startStation, string endStation);
};

My driver program reads commands from standard input. These are: Here are Leetcode's examples:
UNIX> cat input-1.txt
I 45 Leyton 3
I 32 Paradise 8
I 27 Leyton 10
O 45 Waterloo 15
O 27 Waterloo 20
O 32 Cambridge 22
A Paradise Cambridge
A Leyton Waterloo
I 10 Leyton 24
A Leyton Waterloo
O 10 Waterloo 38
A Leyton Waterloo
UNIX> ./a.out < input-1.txt
14
11
11
12
UNIX> cat input-2.txt
I 10 Leyton 3
O 10 Paradise 8
A Leyton Paradise
I 5 Leyton 10
O 5 Paradise 16
A Leyton Paradise
I 2 Leyton 21
O 2 Paradise 30
A Leyton Paradise

UNIX> ./a.out < input-2.txt
5
5.5
6.66667
UNIX> 

Hints

You need two data structures in your class:
  1. A data structure that keeps track of the most recent checkin() for each user.
  2. A data strcuture that keeps track of information to calculate average times for each (ordered) pair of station.
In checkIn(), you add the user, station and time to the data structure, keyed on the user id.

In checkOut(), you find the user's most recent check-in (which is guaranteed to exist, so says the problem writeup), and calculate the time duration from check-in to check-out, and add that information to the second data structure, keyed on the combination of starting station/ending station.

Lets's walk through example one, just to make it explicit what we're storing:

Command First data structure Second data structure Output
I 45 Leyton 3 { (45:Leyton,3) } { }
I 32 Paradise 8 { (32:Paradise 8), (45:Leyton,3) } { }
I 27 Leyton 10 { (27:Leyton,10), (32:Paradise,8), (45:Leyton,3) } { }
O 45 Waterloo 15 { (27:Leyton,10), (32:Paradise,8), (45:Leyton,3) } { (Leyton-Waterloo:{12}) }
O 27 Waterloo 20 { (27:Leyton,10), (32:Paradise,8), (45:Leyton,3) } { (Leyton-Waterloo:{12,10}) }
O 32 Cambridge 22 { (27:Leyton,10), (32:Paradise,8), (45:Leyton,3) } { (Leyton-Waterloo:{12,10}), (Paradise-Cambridge:{14}) }
A Paradise Cambridge { (27:Leyton,10), (32:Paradise,8), (45:Leyton,3) } { (Leyton-Waterloo:{12,10}), (Paradise-Cambridge:{14}) } 14
A Leyton Waterloo { (27:Leyton,10), (32:Paradise,8), (45:Leyton,3) } { (Leyton-Waterloo:{12,10}), (Paradise-Cambridge:{14}) } 11
I 10 Leyton 24 { (10:Leyton,24), (27:Leyton,10), (32:Paradise,8), (45:Leyton,3) } { (Leyton-Waterloo:{12,10}), (Paradise-Cambridge:{14}) }
A Leyton Waterloo { (10:Leyton,24), (27:Leyton,10), (32:Paradise,8), (45:Leyton,3) } { (Leyton-Waterloo:{12,10}), (Paradise-Cambridge:{14}) } 11
O 10 Waterloo 38 { (10:Leyton,24), (27:Leyton,10), (32:Paradise,8), (45:Leyton,3) } { (Leyton-Waterloo:{12,10,14}), (Paradise-Cambridge:{14}) }
A Leyton Waterloo { (10:Leyton,24), (27:Leyton,10), (32:Paradise,8), (45:Leyton,3) } { (Leyton-Waterloo:{12,10,14}), (Paradise-Cambridge:{14}) } 12

Ok -- now that we know what we're storing, let's think about the best way to store it:

I kept the variables in the class -- here are my declarations:

unordered_map <int, pair <int, string> > Checkins;
unordered_map <string, pair <double, int> > Duration_Info;

I didn't make the total an integer, because 1,000,000 * 20,000 can exceed an integer. Also, you should make sure that the starting-station/ending-station string has an "illegal" character separating the two stations (like a hyphen). If you just concatenate the stations, you can get into trouble with something like:

I 1 Fred 0
I 2 FredFred 1
O 1 FredFred 2
O 2 Fred 3