Facebook Pixel

1817. Finding the Users Active Minutes

MediumArrayHash Table
Leetcode Link

Problem Description

You are given logs of user actions on LeetCode and an integer k. The logs are represented as a 2D array logs where each logs[i] = [IDᵢ, timeᵢ] means that the user with ID IDᵢ performed an action at minute timeᵢ.

Key points to understand:

  • Multiple users can perform actions at the same time
  • A single user can perform multiple actions within the same minute
  • User Active Minutes (UAM) for a user is the count of unique minutes in which that user performed any action
  • Each minute is counted only once per user, regardless of how many actions occurred in that minute

Your task is to create a 1-indexed array answer of size k where:

  • answer[j] represents the number of users who have exactly j active minutes
  • The array indices go from 1 to k (1-indexed)

For example, if a user performed actions at minutes [2, 2, 3, 5], their UAM would be 3 (unique minutes: 2, 3, 5). If there are 2 users with UAM = 3, then answer[2] (3rd position in 0-indexed array) would be 2.

The solution uses a hash table to track unique minutes for each user ID. For each user, it stores their active minutes in a set (automatically handling duplicates). Then it counts how many users have each possible UAM value from 1 to k, storing these counts in the result array.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The main challenge is counting unique minutes for each user while handling duplicate entries. When we see that a user might perform multiple actions in the same minute, we need a way to count that minute only once.

This naturally leads us to think about using a set data structure, which automatically handles uniqueness. For each user, we can maintain a set of all the minutes they were active. When we add a minute to a set, duplicates are automatically ignored.

The next insight is that we need to group information by user ID first. Since we're iterating through logs that might have entries from different users mixed together, we need a way to accumulate data for each user separately. A hash table (dictionary) is perfect for this - we can use the user ID as the key and maintain a set of active minutes as the value.

Once we have each user's unique active minutes collected, counting their UAM becomes straightforward - it's just the size of their set. For example, if user 5 has the set {2, 7, 9}, their UAM is 3.

The final step is distribution counting. We need to know how many users have UAM = 1, how many have UAM = 2, and so on up to k. Since UAM values range from 1 to k, and our answer array is 0-indexed, a user with UAM = j goes into answer[j-1]. We iterate through all users' sets, calculate each user's UAM (set size), and increment the corresponding position in the answer array.

This approach elegantly handles all the requirements: duplicate minute filtering through sets, user grouping through hash tables, and distribution counting through array indexing.

Solution Approach

The solution uses a hash table with sets to efficiently track unique active minutes for each user.

Step 1: Initialize the data structure We create a defaultdict(set) where:

  • Keys are user IDs
  • Values are sets containing all unique minutes when that user was active
d = defaultdict(set)

Step 2: Process the logs We iterate through each log entry [i, t] where i is the user ID and t is the timestamp:

for i, t in logs:
    d[i].add(t)

For each log entry, we add the timestamp t to user i's set. Since we're using a set, duplicate timestamps for the same user are automatically handled - if user 5 performs actions at minute 3 twice, the set will only contain one instance of 3.

Step 3: Initialize the answer array We create an array of size k initialized with zeros:

ans = [0] * k

This array will store the distribution of UAM values, where ans[j-1] represents the number of users with UAM equal to j.

Step 4: Count the distribution We iterate through all the sets in our hash table:

for ts in d.values():
    ans[len(ts) - 1] += 1

For each user's set of timestamps ts:

  • len(ts) gives us the UAM (number of unique minutes)
  • Since the answer array is 0-indexed but we need 1-indexed results, we use len(ts) - 1 as the index
  • We increment the count at that position

Example walkthrough: If logs = [[1,2], [1,2], [1,3], [2,5], [2,6]] and k = 3:

  1. After processing: d = {1: {2, 3}, 2: {5, 6}}
  2. User 1 has UAM = 2 (two unique minutes)
  3. User 2 has UAM = 2 (two unique minutes)
  4. ans[1] += 2 (two users with UAM = 2)
  5. Final result: [0, 2, 0]

The time complexity is O(n) where n is the number of logs, and space complexity is O(n) for storing the hash table and sets.

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a concrete example with logs = [[1,3], [2,3], [1,3], [1,7], [2,5], [3,5]] and k = 4.

Step 1: Build the hash table with sets

We process each log entry and add timestamps to each user's set:

  • [1,3]: Add minute 3 to user 1's set → d = {1: {3}}
  • [2,3]: Add minute 3 to user 2's set → d = {1: {3}, 2: {3}}
  • [1,3]: Add minute 3 to user 1's set (duplicate, ignored) → d = {1: {3}, 2: {3}}
  • [1,7]: Add minute 7 to user 1's set → d = {1: {3, 7}, 2: {3}}
  • [2,5]: Add minute 5 to user 2's set → d = {1: {3, 7}, 2: {3, 5}}
  • [3,5]: Add minute 5 to user 3's set → d = {1: {3, 7}, 2: {3, 5}, 3: {5}}

Final hash table: {1: {3, 7}, 2: {3, 5}, 3: {5}}

Step 2: Calculate UAM for each user

  • User 1: {3, 7} → UAM = 2 unique minutes
  • User 2: {3, 5} → UAM = 2 unique minutes
  • User 3: {5} → UAM = 1 unique minute

Step 3: Build the answer array

Initialize: ans = [0, 0, 0, 0] (size k=4)

Count distribution:

  • User 1 has UAM = 2 → increment ans[2-1]ans = [0, 1, 0, 0]
  • User 2 has UAM = 2 → increment ans[2-1]ans = [0, 2, 0, 0]
  • User 3 has UAM = 1 → increment ans[1-1]ans = [1, 2, 0, 0]

Final answer: [1, 2, 0, 0]

This means:

  • 1 user has exactly 1 active minute (User 3)
  • 2 users have exactly 2 active minutes (Users 1 and 2)
  • 0 users have exactly 3 active minutes
  • 0 users have exactly 4 active minutes

The key insight demonstrated here is how the set automatically handled the duplicate entry [1,3] appearing twice, ensuring user 1's minute 3 was only counted once in their UAM calculation.

Solution Implementation

1from collections import defaultdict
2from typing import List
3
4class Solution:
5    def findingUsersActiveMinutes(self, logs: List[List[int]], k: int) -> List[int]:
6        # Dictionary to store unique minutes for each user
7        # Key: user_id, Value: set of unique minutes
8        user_minutes = defaultdict(set)
9      
10        # Process each log entry
11        for user_id, minute in logs:
12            # Add the minute to the user's set of active minutes
13            user_minutes[user_id].add(minute)
14      
15        # Initialize result array with k zeros
16        # result[i] represents count of users with UAM = i+1
17        result = [0] * k
18      
19        # Count users by their UAM (User Active Minutes)
20        for unique_minutes in user_minutes.values():
21            # Get the UAM count for this user
22            uam_count = len(unique_minutes)
23            # Increment the count for this UAM value (0-indexed)
24            result[uam_count - 1] += 1
25      
26        return result
27
1class Solution {
2    public int[] findingUsersActiveMinutes(int[][] logs, int k) {
3        // Map to store user ID as key and set of unique active minutes as value
4        Map<Integer, Set<Integer>> userToMinutesMap = new HashMap<>();
5      
6        // Process each log entry
7        for (int[] log : logs) {
8            int userId = log[0];
9            int minute = log[1];
10          
11            // Add the minute to the user's set of active minutes
12            // If user doesn't exist in map, create a new HashSet first
13            userToMinutesMap.computeIfAbsent(userId, key -> new HashSet<>()).add(minute);
14        }
15      
16        // Initialize result array to count users by their UAM (User Active Minutes)
17        int[] result = new int[k];
18      
19        // Count how many users have each UAM value
20        for (Set<Integer> minutesSet : userToMinutesMap.values()) {
21            int uamCount = minutesSet.size();  // Number of unique active minutes for this user
22            result[uamCount - 1]++;  // Increment count for this UAM value (0-indexed)
23        }
24      
25        return result;
26    }
27}
28
1class Solution {
2public:
3    vector<int> findingUsersActiveMinutes(vector<vector<int>>& logs, int k) {
4        // Map to store each user's unique active minutes
5        // Key: user ID, Value: set of unique minutes when user performed actions
6        unordered_map<int, unordered_set<int>> userToMinutes;
7      
8        // Process each log entry
9        for (auto& log : logs) {
10            int userId = log[0];
11            int minute = log[1];
12            // Add the minute to user's set of active minutes (duplicates automatically handled by set)
13            userToMinutes[userId].insert(minute);
14        }
15      
16        // Initialize result array with k elements, all set to 0
17        // result[i] represents count of users with UAM (User Active Minutes) = i+1
18        vector<int> result(k);
19      
20        // Count users for each UAM value
21        for (auto& [userId, minutesSet] : userToMinutes) {
22            int uam = minutesSet.size();  // User's active minutes count
23            // Increment count for this UAM value (1-indexed to 0-indexed conversion)
24            ++result[uam - 1];
25        }
26      
27        return result;
28    }
29};
30
1/**
2 * Calculates the distribution of users based on their unique active minutes (UAM)
3 * @param logs - Array of [userId, minute] pairs representing user actions
4 * @param k - Maximum number of unique active minutes to track
5 * @returns Array where index i contains the count of users with exactly i+1 UAM
6 */
7function findingUsersActiveMinutes(logs: number[][], k: number): number[] {
8    // Map to store unique minutes for each user
9    // Key: userId, Value: Set of unique minutes when user was active
10    const userToMinutesMap: Map<number, Set<number>> = new Map();
11  
12    // Process each log entry to build the map of users and their unique active minutes
13    for (const [userId, minute] of logs) {
14        // Initialize a new Set for the user if not already present
15        if (!userToMinutesMap.has(userId)) {
16            userToMinutesMap.set(userId, new Set<number>());
17        }
18        // Add the minute to the user's set of active minutes (duplicates are automatically handled by Set)
19        userToMinutesMap.get(userId)!.add(minute);
20    }
21  
22    // Initialize result array with k elements, all set to 0
23    const result: number[] = Array(k).fill(0);
24  
25    // Count how many users have each number of unique active minutes
26    for (const [_, minutesSet] of userToMinutesMap) {
27        // The size of the set represents the user's UAM count
28        // Increment the count at index (UAM - 1) since array is 0-indexed
29        ++result[minutesSet.size - 1];
30    }
31  
32    return result;
33}
34

Time and Space Complexity

The time complexity is O(n), where n is the length of the logs array. This is because:

  • We iterate through all n log entries once to populate the dictionary d, with each insertion into a set taking O(1) average time
  • We then iterate through all unique users in d.values(), which is at most n iterations
  • Each operation inside both loops takes constant time

The space complexity is O(n), where n is the length of the logs array. This is because:

  • The dictionary d can store at most n unique user-time pairs in the worst case (when all logs have different users or different times)
  • The ans array has a fixed size of k, which is independent of n
  • Since k is typically much smaller than n and bounded, the dominant space usage comes from the dictionary d

Learn more about how to find time and space complexity quickly.

Common Pitfalls

Pitfall 1: Confusing User Count with Action Count

The Problem: A common mistake is counting the total number of actions per user instead of counting unique minutes. For example, if user 1 performs actions at [2, 2, 3], beginners might count this as 3 actions instead of 2 unique minutes.

Incorrect Approach:

user_actions = defaultdict(int)
for user_id, minute in logs:
    user_actions[user_id] += 1  # Wrong! Counts total actions, not unique minutes

Correct Solution:

user_minutes = defaultdict(set)
for user_id, minute in logs:
    user_minutes[user_id].add(minute)  # Set automatically handles uniqueness

Pitfall 2: Index Confusion (0-indexed vs 1-indexed)

The Problem: The problem asks for a 1-indexed conceptual answer but Python arrays are 0-indexed. If a user has UAM = 3, this should go into result[2] (the third position in a 0-indexed array).

Incorrect Approach:

for unique_minutes in user_minutes.values():
    uam_count = len(unique_minutes)
    result[uam_count] += 1  # Wrong! This will cause IndexError or wrong placement

Correct Solution:

for unique_minutes in user_minutes.values():
    uam_count = len(unique_minutes)
    result[uam_count - 1] += 1  # Subtract 1 to convert to 0-indexed

Pitfall 3: Out of Bounds Access

The Problem: Not handling cases where a user's UAM exceeds k. While the problem typically ensures this won't happen, defensive programming is good practice.

Risky Approach:

result[uam_count - 1] += 1  # Could fail if uam_count > k

Defensive Solution:

for unique_minutes in user_minutes.values():
    uam_count = len(unique_minutes)
    if 1 <= uam_count <= k:  # Ensure we're within bounds
        result[uam_count - 1] += 1

Pitfall 4: Using List Instead of Set for Tracking Minutes

The Problem: Using a list to store minutes requires manual duplicate checking, making the code more complex and less efficient.

Inefficient Approach:

user_minutes = defaultdict(list)
for user_id, minute in logs:
    if minute not in user_minutes[user_id]:  # Manual duplicate check
        user_minutes[user_id].append(minute)

Efficient Solution:

user_minutes = defaultdict(set)
for user_id, minute in logs:
    user_minutes[user_id].add(minute)  # Set handles duplicates automatically
Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

Which of the following problems can be solved with backtracking (select multiple)


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More