Problem: Alert Using Same Key-Card Three or More Times in a One Hour Period

LeetCode company workers use key-cards to unlock office doors. Each time a
worker uses their key-card, the security system saves the worker's name and
the time when it was used. The system emits an alert if any worker uses the
key-card three or more times in a one-hour period.

You are given a list of strings `keyName` and `keyTime` where `[keyName[i],
keyTime[i]]` corresponds to a person's name and the time when their key-card
was used in a single day.

Access times are given in the 24-hour time format "HH:MM", such as `"23:51"`
and `"09:49"`.

Return a list of unique worker names who received an alert for frequent
keycard use. Sort the names in ascending order alphabetically.

Notice that `"10:00"` \- `"11:00"` is considered to be within a one-hour
period, while `"22:51"` \- `"23:52"` is not considered to be within a one-hour
period.


Python solution:
import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def alertNames(self, keyName: List[str], keyTime: List[str]) -> List[str]:
    nameToMinutes = collections.defaultdict(list)

    for name, time in zip(keyName, keyTime):
      minutes = self._getMinutes(time)
      nameToMinutes[name].append(minutes)

    res=[]
    for name, minutes in nameToMinutes.items():
      if self._hasAlert(minutes):
        res.append(name)
    return sorted(res)

  def _hasAlert(self, minutes: List[int]) -> bool:
    if len(minutes) > 70:
      return True
    minutes.sort()
    for i in range(2, len(minutes)):
      if minutes[i - 2] + 60 >= minutes[i]:
        return True
    return False

  def _getMinutes(self, time: str) -> int:
    h, m = map(int, time.split(':'))
    return 60 * h + m


Java solution:
class Solution {
  public List<String> alertNames(String[] keyName, String[] keyTime) {
    List<String> ans = new ArrayList<>();
    HashMap<String, List<Integer>> nameToMinutes = new HashMap<>();

    for (int i = 0; i < keyName.length; i++) {
      final int minutes = getMinutes(keyTime[i]);
      nameToMinutes.putIfAbsent(keyName[i], new ArrayList<>());
      nameToMinutes.get(keyName[i]).add(minutes);
    }

    for (Map.Entry<String, List<Integer>> entry : nameToMinutes.entrySet()) {
      final String name = entry.getKey();
      List<Integer> minutes = entry.getValue();
      if (hasAlert(minutes))
        ans.add(name);
    }

    Collections.sort(ans);
    return ans;
  }

  private boolean hasAlert(List<Integer> minutes) {
    if (minutes.size() > 70)
      return true;
    Collections.sort(minutes);
    for (int i = 2; i < minutes.size(); i++)
      if (minutes.get(i - 2) + 60 >= minutes.get(i))
        return true;
    return false;
  }

  private int getMinutes(String time) {
    final int h = Integer.parseInt(time.substring(0, 2));
    final int m = Integer.parseInt(time.substring(3));
    return 60 * h + m;
  }
}


C++ solution:
class Solution {
 public:
  vector<string> alertNames(vector<string>& keyName, vector<string>& keyTime) {
    vector<string> ans;
    unordered_map<string, vector<int>> nameToMinutes;

    for (int i = 0; i < keyName.size(); ++i) {
      const int minutes = getMinutes(keyTime[i]);
      nameToMinutes[keyName[i]].push_back(minutes);
    }

    for (auto& [name, minutes] : nameToMinutes)
      if (hasAlert(minutes))
        ans.push_back(name);

    ranges::sort(ans);
    return ans;
  }

 private:
  // Returns true if any worker uses the key-card three or more times in an
  // one-hour period.
  bool hasAlert(vector<int>& minutes) {
    if (minutes.size() > 70)
      return true;
    ranges::sort(minutes);
    for (int i = 2; i < minutes.size(); ++i)
      if (minutes[i - 2] + 60 >= minutes[i])
        return true;
    return false;
  }

  int getMinutes(const string& time) {
    const int h = stoi(time.substr(0, 2));
    const int m = stoi(time.substr(3));
    return 60 * h + m;
  }
};