Python 1604.py
class Solution:
def alertNames(self, keyName: List[str], keyTime: List[str]) -> List[str]:
# one hour alert, measured in minutes
threshold = 60
# split into names
all = defaultdict(list)
for n, t in zip(keyName, keyTime):
# convert HH:MM to minutes since midnight
a, b = t.split(":")
minutes = int(a) * 60 + int(b)
all[n].append(minutes)
# and sort
for user in sorted(all):
one = two = -inf
for three in sorted(all[user]):
if three - one <= 60:
yield user
break
one = two
two = three
Difficulty
and has an acceptance rate of about 46% (100k+ submissions).
Typical patterns to solve this problem:
- Array
- Hash Table
- Sorting
- String
AI Disclaimer
However, two years later I set up this website and used various AI tools to generate most of the walkthrough / explanations below.
See this dedicated page for more details.
Code Walkthrough
Overview
The provided Solution class implements a method that identifies employee names who used a key card three or more times within any one-hour window. The method receives two parallel lists: keyName, containing employee identifiers, and keyTime, containing timestamps in "HH:MM" format. It returns a list of distinct employee names that meet the alert condition, sorted alphabetically.
Data Preparation
The first logical block groups timestamps by employee name.
all = defaultdict(list)
for n, t in zip(keyName, keyTime):
a, b = t.split(":")
minutes = int(a) * 60 + int(b)
all[n].append(minutes)
Each timestamp string is split at the colon, converting hours and minutes into a single integer representing minutes since midnight. The resulting integer is appended to a list associated with the employee’s name in a defaultdict. This conversion simplifies subsequent arithmetic comparisons.
Sorting for Deterministic Traversal
Before scanning for alerts, the code sorts the employee keys and the individual timestamp lists.
for user in sorted(all):
for three in sorted(all[user]):
...
Sorting the keys ensures that the final output respects alphabetical order without an extra sorting step at the end. Sorting each user’s timestamps guarantees that the sliding-window check proceeds chronologically, which is essential for correctly detecting a one-hour interval.
Sliding-Window Detection
The core detection logic maintains three variables that represent the most recent three timestamps encountered for a given user.
one = two = -inf
for three in sorted(all[user]):
if three - one <= 60:
yield user
break
one = two
two = three
Initially, one and two are set to negative infinity, ensuring that the first two iterations cannot falsely trigger the alert condition. As the loop iterates over sorted timestamps, the difference between the current timestamp (three) and the timestamp three positions earlier (one) is evaluated. If this difference does not exceed the 60-minute threshold, the user satisfies the alert criterion, and the generator yields the user’s name, terminating further checks for that user.
Edge-Case Handling
The conversion of "HH:MM" to minutes correctly handles times at the start of the day ("00:00") and at the end of the day ("23:59"), because the arithmetic is based on a 24-hour clock expressed in total minutes. The use of -inf for the initial placeholders prevents accidental matches when fewer than three timestamps exist for a user; the condition three - one <= 60 can never be true until three genuine timestamps have been processed. Additionally, the early break after yielding a user ensures that users with many qualifying windows are reported only once, avoiding duplicate entries.
Performance Optimizations
Grouping timestamps with a defaultdict avoids repeated existence checks and list initializations, providing constant-time insertion per record. Converting timestamps to integer minutes up front eliminates repeated string parsing during the sliding-window scan, reducing overhead. Sorting each user’s timestamp list once enables a linear pass with a fixed-size sliding window, avoiding nested loops that would otherwise increase work. The early exit (break) after detecting an alert for a user prevents unnecessary iteration over the remainder of that user’s timestamps, saving cycles when the condition is met early in the list.
Output Generation
The method uses a generator (yield) inside the loop, allowing the caller to collect results lazily. When the generator is consumed (e.g., via list(solution.alertNames(...))), the yielded names are already in alphabetical order because the outer loop iterates over sorted(all). This design eliminates the need for a separate sorting step on the final result set.
ELI5 - Simplified Explanation
Imagine a big notebook that tracks when people use their access badges to enter a room. The code starts by reading through all the entries in the notebook. It takes the names of the people and the times they entered, like ten fifteen, and translates those times into just the number of minutes that have passed since the day began. This makes it much easier to compare the times later on.
All the entry times for each person are put together into little lists. Once the lists are ready, the code looks at each person one by one to see how they used their badge. To make sure the check is fair, the code keeps the entry times in order from the beginning of the day to the end.
As it goes through the times for a single person, it keeps track of the three most recent times used to enter the room. If it notices that the person entered the room three times within a single hour, it marks them down for an alert. It does this by checking if the time of the third entry is close enough to the time of the first entry in that set. If everything matches up, the person gets an alert for using their badge too many times in a row.
Code Quality
Complexity Analysis
As a rule of thumb, the maximum input size for one second execution time is
- factorial: up to 10 elements
- exponential: up to 20 elements
- polynomial: a few thousand elements
- loglinear: up to 100,000 elements
- linear: a few million elements
- logarithmic: a billion elements
- constant: practically unlimited
Other Solutions
Obviously, I did that after I solved the problem myself.
In general, AlgoMonster and WalkCCC solutions are high-quality and always worth a look.
I haven't checked most of the other solutions, but they might be helpful as well.
And of course, the LeetCode forum is always a good place (or even the best) to look for alternative approaches.
(click on a column header to sort the table)
| author | ||||||||
|---|---|---|---|---|---|---|---|---|
| author | solutions | |||||||
| www | AlgoMonster | C++ | Python | Java | TS | |||
| WalkCCC | C++ | Python | Java | |||||
| leetcode.ca | C++ | Python | Java | Go | TS | |||
| Progiez | C++ | Python | Java | |||||
| github | black-shadows | C++ | ||||||
| charles-wangkai | Java | |||||||
| fsq | C++ | |||||||
| JoshCrozier | JS | |||||||
| keineahnung2345 | C++ | |||||||
| lkwq007 | Python | |||||||
| suxrobGM | C# | |||||||
| trung-hn | Python | |||||||
| varunu28 | Java | |||||||
| AlgoMonster | C++, Python, Java, TypeScript | |||||||
| WalkCCC | C++, Python, Java | |||||||
| leetcode.ca | C++, Go, Java, Python, TypeScript | |||||||
| Progiez | C++, Java, Python | |||||||
| black-shadows | C++, C++ | |||||||
| charles-wangkai | Java | |||||||
| fsq | C++ | |||||||
| JoshCrozier | JavaScript | |||||||
| keineahnung2345 | C++ | |||||||
| lkwq007 | Python | |||||||
| suxrobGM | C# | |||||||
| trung-hn | Python | |||||||
| varunu28 | Java |
Videos
Solved On The Same Day
- problem 1400: Construct K Palindrome Strings
- problem 1442: Count Triplets That Can Form Two Arrays of Equal XOR
- problem 1519: Number of Nodes in the Sub-Tree With the Same Label
- problem 1577: Number of Ways Where Square of Number Is Equal to Product of Two Numbers
- problem 1593: Split a String Into the Max Number of Unique Substrings
- problem 1638: Count Substrings That Differ by One Character
- problem 1695: Maximum Erasure Value
- problem 1726: Tuple with Same Product
- problem 1368: Minimum Cost to Make at Least One Valid Path in a Grid
Extra
- Tech Stack - my development environment and tools
- AI - how AI helped me create this website (NOT solving the problems)
- Progress - progression over time
- Quality - code quality