2933. High-Access Employees
Problem Description
You are given a 2D array access_times
where each element contains an employee's name and their access time to a system. Each access_times[i]
has two parts:
access_times[i][0]
: employee nameaccess_times[i][1]
: access time in 24-hour format (e.g., "0800", "2250")
All access times are from the same day.
An employee is considered high-access if they accessed the system three or more times within any one-hour period.
Important rules for the one-hour period:
- The one-hour period is strictly less than 60 minutes. For example, accesses at "0815" and "0915" are NOT within the same one-hour period (exactly 60 minutes apart).
- Access times at the start and end of the day don't wrap around. For example, "0005" and "2350" are NOT considered within the same one-hour period.
Your task is to return a list of names of all high-access employees. The order of names in the output doesn't matter.
For example, if an employee has access times at "0810", "0820", and "0830", they would be high-access since all three times fall within a 60-minute window (from 0810 to before 0910). However, if the times were "0810", "0820", and "0910", they would NOT be high-access since the third access is exactly 60 minutes after the first.
Intuition
The key insight is that we need to check if any employee has 3 or more accesses within a sliding window of less than 60 minutes.
First, we need to group all access times by employee name since the same employee might appear multiple times in the input. A hash table (dictionary) is perfect for this grouping.
For time comparisons, working with the string format "HHMM" is cumbersome. Converting each time to minutes since midnight makes calculations much simpler. For example, "0830" becomes 8 * 60 + 30 = 510
minutes. This conversion allows us to easily check if times are within 60 minutes of each other.
Once we have all access times for each employee, we need to find if there are 3 or more accesses within any 60-minute window. A naive approach would be to check every possible window, but there's a clever optimization: after sorting the access times, we only need to check if any 3 consecutive accesses fall within 60 minutes.
Why does checking consecutive accesses work? Because if we have accesses at times t1 < t2 < t3 < t4
, and t1, t2, t4
are within 60 minutes, then t1, t2, t3
must also be within 60 minutes (since t3 < t4
). So we won't miss any valid windows by only checking consecutive groups of 3.
The condition ts[i] - ts[i-2] < 60
elegantly checks if the current access and the access two positions before are within 60 minutes. If true, then these three consecutive accesses (ts[i-2]
, ts[i-1]
, ts[i]
) form a high-access pattern.
Learn more about Sorting patterns.
Solution Approach
The implementation follows these steps:
-
Create a hash table to group access times by employee: We use a
defaultdict(list)
where the key is the employee name and the value is a list to store all their access times. -
Convert time strings to minutes: For each access record
[name, time]
, we parse the time string and convert it to minutes since midnight:- Extract hours:
int(t[:2])
gets the first two characters as hours - Extract minutes:
int(t[2:])
gets the last two characters as minutes - Total minutes:
hours * 60 + minutes
This conversion is stored in the hash table:
d[name].append(int(t[:2]) * 60 + int(t[2:]))
- Extract hours:
-
Process each employee's access times: Iterate through the hash table entries:
- Sort each employee's access times in ascending order using
ts.sort()
- This sorting is crucial as it allows us to check only consecutive groups of 3 accesses
- Sort each employee's access times in ascending order using
-
Check for high-access pattern: For each employee's sorted times, we check if any three consecutive accesses are within 60 minutes:
- Start from index 2 (since we need to look back 2 positions)
- Check condition:
ts[i] - ts[i-2] < 60
- If the difference between the current time and the time two positions back is less than 60, then these three consecutive times (
ts[i-2]
,ts[i-1]
,ts[i]
) form a high-access window - Use
any()
function to check if at least one such window exists
-
Build the result: If an employee has at least one high-access window, add their name to the answer list.
The time complexity is O(n log n)
for each employee due to sorting, where n
is the number of accesses per employee. The space complexity is O(n)
to store all access times.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
Input: access_times = [["Alice","0845"], ["Alice","0910"], ["Alice","0920"], ["Bob","1000"], ["Bob","1040"], ["Bob","1050"]]
Step 1: Group access times by employee We create a hash table to store each employee's access times:
- Initialize empty dictionary
d
- Process each entry:
["Alice","0845"]
βd["Alice"] = [525]
(where 525 = 8Γ60 + 45)["Alice","0910"]
βd["Alice"] = [525, 550]
(where 550 = 9Γ60 + 10)["Alice","0920"]
βd["Alice"] = [525, 550, 560]
(where 560 = 9Γ60 + 20)["Bob","1000"]
βd["Bob"] = [600]
(where 600 = 10Γ60 + 0)["Bob","1040"]
βd["Bob"] = [600, 640]
(where 640 = 10Γ60 + 40)["Bob","1050"]
βd["Bob"] = [600, 640, 650]
(where 650 = 10Γ60 + 50)
Step 2: Check each employee for high-access pattern
For Alice:
- Times in minutes:
[525, 550, 560]
(already sorted) - Check windows of 3 consecutive accesses:
- At index 2: Check if
560 - 525 < 60
560 - 525 = 35 < 60
β- This means times at 0845, 0910, and 0920 are all within 60 minutes
- At index 2: Check if
- Alice is a high-access employee
For Bob:
- Times in minutes:
[600, 640, 650]
(already sorted) - Check windows of 3 consecutive accesses:
- At index 2: Check if
650 - 600 < 60
650 - 600 = 50 < 60
β- This means times at 1000, 1040, and 1050 are all within 60 minutes
- At index 2: Check if
- Bob is a high-access employee
Step 3: Return result Both Alice and Bob are high-access employees.
Output: ["Alice", "Bob"]
The key insight here is that by converting times to minutes and sorting them, we only need to check if the first and third accesses in any consecutive group of three are less than 60 minutes apart. This efficiently identifies all employees who accessed the system three or more times within any one-hour window.
Solution Implementation
1from collections import defaultdict
2from typing import List
3
4class Solution:
5 def findHighAccessEmployees(self, access_times: List[List[str]]) -> List[str]:
6 # Dictionary to store access times for each employee
7 employee_access_times = defaultdict(list)
8
9 # Convert time strings to minutes and group by employee name
10 for name, time_str in access_times:
11 # Convert "HHMM" format to total minutes since midnight
12 hours = int(time_str[:2])
13 minutes = int(time_str[2:])
14 total_minutes = hours * 60 + minutes
15 employee_access_times[name].append(total_minutes)
16
17 # List to store employees with high access frequency
18 high_access_employees = []
19
20 # Check each employee's access pattern
21 for name, time_list in employee_access_times.items():
22 # Sort access times in ascending order
23 time_list.sort()
24
25 # Check if any 3 consecutive accesses occur within 60 minutes
26 # We check if the difference between the i-th and (i-2)-th access is less than 60
27 for i in range(2, len(time_list)):
28 if time_list[i] - time_list[i - 2] < 60:
29 high_access_employees.append(name)
30 break # No need to check further for this employee
31
32 return high_access_employees
33
1class Solution {
2 public List<String> findHighAccessEmployees(List<List<String>> access_times) {
3 // Map to store employee names and their access times in minutes
4 Map<String, List<Integer>> employeeAccessMap = new HashMap<>();
5
6 // Process each access record
7 for (List<String> accessRecord : access_times) {
8 String employeeName = accessRecord.get(0);
9 String timeString = accessRecord.get(1);
10
11 // Convert time from "HHMM" format to total minutes
12 int hours = Integer.valueOf(timeString.substring(0, 2));
13 int minutes = Integer.valueOf(timeString.substring(2));
14 int totalMinutes = hours * 60 + minutes;
15
16 // Add the time to the employee's access list
17 employeeAccessMap.computeIfAbsent(employeeName, k -> new ArrayList<>())
18 .add(totalMinutes);
19 }
20
21 // List to store high-access employees
22 List<String> highAccessEmployees = new ArrayList<>();
23
24 // Check each employee's access pattern
25 for (Map.Entry<String, List<Integer>> entry : employeeAccessMap.entrySet()) {
26 String employeeName = entry.getKey();
27 List<Integer> accessTimesList = entry.getValue();
28
29 // Sort access times in ascending order
30 Collections.sort(accessTimesList);
31
32 // Check if any 3 consecutive accesses occur within 60 minutes
33 for (int i = 2; i < accessTimesList.size(); i++) {
34 // Compare current access time with the one from 2 positions before
35 if (accessTimesList.get(i) - accessTimesList.get(i - 2) < 60) {
36 highAccessEmployees.add(employeeName);
37 break; // Found high access pattern, no need to check further
38 }
39 }
40 }
41
42 return highAccessEmployees;
43 }
44}
45
1class Solution {
2public:
3 vector<string> findHighAccessEmployees(vector<vector<string>>& access_times) {
4 // Map to store employee names and their access times in minutes
5 unordered_map<string, vector<int>> employeeAccessMap;
6
7 // Convert each access time to minutes and group by employee name
8 for (auto& entry : access_times) {
9 string employeeName = entry[0];
10 string timeString = entry[1];
11
12 // Convert time from "HHMM" format to total minutes
13 int hours = stoi(timeString.substr(0, 2));
14 int minutes = stoi(timeString.substr(2, 2));
15 int totalMinutes = hours * 60 + minutes;
16
17 employeeAccessMap[employeeName].emplace_back(totalMinutes);
18 }
19
20 // Result vector to store high-access employees
21 vector<string> highAccessEmployees;
22
23 // Check each employee's access pattern
24 for (auto& [employeeName, accessTimesList] : employeeAccessMap) {
25 // Sort access times in ascending order
26 sort(accessTimesList.begin(), accessTimesList.end());
27
28 // Check if any 3 consecutive accesses occur within 60 minutes
29 for (int i = 2; i < accessTimesList.size(); ++i) {
30 // If the difference between current and (i-2)th access is less than 60 minutes,
31 // this employee has 3 accesses within a one-hour window
32 if (accessTimesList[i] - accessTimesList[i - 2] < 60) {
33 highAccessEmployees.emplace_back(employeeName);
34 break; // Found high-access pattern, no need to check further
35 }
36 }
37 }
38
39 return highAccessEmployees;
40 }
41};
42
1/**
2 * Finds employees who have accessed the system 3 or more times within any 60-minute window
3 * @param access_times - Array of [employee_name, access_time] pairs where access_time is in "HHMM" format
4 * @returns Array of employee names who are high-access employees
5 */
6function findHighAccessEmployees(access_times: string[][]): string[] {
7 // Map to store each employee's access times in minutes
8 const employeeAccessMap: Map<string, number[]> = new Map();
9
10 // Convert access times to minutes and group by employee
11 for (const [employeeName, timeString] of access_times) {
12 // Extract hours and minutes from the time string
13 const hours = parseInt(timeString.slice(0, 2), 10);
14 const minutes = parseInt(timeString.slice(2), 10);
15
16 // Convert time to total minutes since midnight
17 const totalMinutes = hours * 60 + minutes;
18
19 // Initialize array for new employee
20 if (!employeeAccessMap.has(employeeName)) {
21 employeeAccessMap.set(employeeName, []);
22 }
23
24 // Add the access time to the employee's list
25 employeeAccessMap.get(employeeName)!.push(totalMinutes);
26 }
27
28 // Result array to store high-access employees
29 const highAccessEmployees: string[] = [];
30
31 // Check each employee's access pattern
32 for (const [employeeName, accessTimes] of employeeAccessMap) {
33 // Sort access times in ascending order
34 accessTimes.sort((a, b) => a - b);
35
36 // Check if any 3 consecutive accesses occur within 60 minutes
37 for (let i = 2; i < accessTimes.length; i++) {
38 // If the difference between current and 2 accesses ago is less than 60 minutes
39 if (accessTimes[i] - accessTimes[i - 2] < 60) {
40 highAccessEmployees.push(employeeName);
41 break; // Found high-access pattern, no need to check further
42 }
43 }
44 }
45
46 return highAccessEmployees;
47}
48
Time and Space Complexity
Time Complexity: O(n Γ log n)
The algorithm consists of the following steps:
- Iterating through all access records to build the dictionary:
O(n)
, wheren
is the total number of access records - For each employee, sorting their access times: Let's say there are
k
unique employees, and each employee has on averagen/k
access times. The sorting for each employee takesO((n/k) Γ log(n/k))
. For all employees combined:O(k Γ (n/k) Γ log(n/k)) = O(n Γ log(n/k))
- Checking consecutive access times for each employee:
O(n)
in total across all employees
In the worst case, when one employee has all n
access records, the sorting step becomes O(n Γ log n)
, which dominates the overall time complexity.
Space Complexity: O(n)
The space is used for:
- The dictionary
d
which stores all access times for each employee:O(n)
space to storen
access records - The answer list
ans
:O(k)
space wherek
is the number of unique employees, andk β€ n
- The sorted lists are created in-place using the existing storage in the dictionary
Therefore, the total space complexity is O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding the "within one hour" requirement
Pitfall: Many developers incorrectly interpret "within one hour" as including exactly 60 minutes. For instance, they might consider accesses at "0815" and "0915" as being within the same one-hour period.
Why it happens: The natural inclination is to use <=
when checking time differences, thinking that 60 minutes equals one hour.
Incorrect implementation:
# WRONG: This includes exactly 60 minutes if time_list[i] - time_list[i - 2] <= 60: # Bug: should be < 60 high_access_employees.append(name)
Correct approach: Use strict inequality (< 60
) since the problem specifies "strictly less than 60 minutes":
# CORRECT: Strictly less than 60 minutes if time_list[i] - time_list[i - 2] < 60: high_access_employees.append(name)
2. Not handling duplicate names in the result
Pitfall: If an employee has multiple high-access windows, they might be added to the result list multiple times without the break
statement.
Example scenario: An employee with times ["0800", "0810", "0820", "0830", "0840"] has multiple overlapping windows where 3 accesses occur within 60 minutes.
Incorrect implementation:
for i in range(2, len(time_list)):
if time_list[i] - time_list[i - 2] < 60:
high_access_employees.append(name)
# Missing break - employee could be added multiple times
Correct approach: Add a break
statement once a high-access pattern is found:
for i in range(2, len(time_list)):
if time_list[i] - time_list[i - 2] < 60:
high_access_employees.append(name)
break # Prevent duplicate entries
3. Forgetting to sort the access times
Pitfall: Processing access times in the order they appear in the input without sorting can miss valid high-access patterns.
Example: If input is [["Alice", "0830"], ["Alice", "0810"], ["Alice", "0820"]]
, checking consecutive pairs without sorting would fail to detect the high-access pattern.
Solution: Always sort each employee's access times before checking for patterns:
time_list.sort() # Critical step - don't skip this!
4. Incorrect time string parsing
Pitfall: Assuming time format or using incorrect string slicing indices.
Common mistakes:
# WRONG: Incorrect slicing
hours = int(time_str[0:2]) # Works but inconsistent
minutes = int(time_str[2:4]) # Should use [2:] for clarity
# WRONG: Not handling potential edge cases
hours = int(time_str.split(':')[0]) # Assumes colon separator
Correct approach: Use consistent and clear slicing:
hours = int(time_str[:2]) # First two characters
minutes = int(time_str[2:]) # Last two characters
What are the most two important steps in writing a depth first search function? (Select 2)
Recommended Readings
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Donβt Miss This!