2284. Sender With Largest Word Count

MediumArrayHash TableStringCounting
Leetcode Link

Problem Description

In the given problem, we are working with a log of chat messages. We have two arrays: messages and senders. Each element of the messages array is a string representing a single chat message, and the corresponding element in the senders array is the name of the person who sent that message. It's important to note that these messages are well-formed, meaning they do not have any leading or trailing spaces and words in a message are separated by a single space.

The task is to determine which sender has the highest word count across all their messages. The word count for a sender is simply the total count of all words that sender has sent, considering all messages. If it turns out that multiple senders have the same highest word count, then we must return the sender whose name is the lexicographically largest among them. Here, lexicographical order refers to how words are sorted in a dictionary, with the catch that uppercase letters are considered to precede lowercase ones, thus 'A' < 'a'.

In essence, we are asked to count words for each sender, compare the totals, and identify the sender with either the highest word count or, in case of a tie, the "largest" name.

Intuition

To approach this problem, we should think about how to effectively track the word count for each sender. A natural choice for this task is to use a hashmap (or a dictionary in Python), where the keys are the senders' names, and the values are their respective word counts.

Since we are provided pairs of messages and senders, we can iterate through these pairs and for each pair:

  1. Count the words in the message. Since words are separated by spaces, we can simply count how many spaces are in the message and add one to that to get the word count.
  2. Update the word count for the sender in the hashmap. If the sender isn't in the hashmap yet, we initialize their word count with the current count; otherwise, we add to the existing count.

After processing all pairs, we'll have a complete hashmap of senders and their total word counts.

The next step is to find the sender with the largest word count while keeping in mind the rules for breaking ties. To do this, we can iterate over the hashmap entries and keep track of the sender with the highest word count seen so far. If we find a sender with the same word count as our current highest, we check the lexicographical order between the names to decide if we should update our answer to this sender. Ultimately, we will arrive at the final answer after checking all the entries.

Not Sure What to Study? Take the 2-min Quiz to Find Your Missing Piece:

What are the most two important steps in writing a depth first search function? (Select 2)

Solution Approach

The implemented solution uses Python's Counter class from the collections module, which is a specialized dictionary designed for counting hashable objects. It's an apt choice for maintaining the word counts for different senders in our problem.

Here's the step-by-step breakdown of the implementation:

  1. Initialize a Counter object cnt. This will hold our senders as keys and their word counts as values.

  2. Iterate over the zipped messages and senders arrays. The zip function is used to create pairs of message-sender which allows us to process them together.

  3. In the loop, increment the word count for each sender in the Counter object by the number of words in their corresponding msg. Since words in a message are separated by spaces, msg.count(' ') + 1 gives us the number of words in the message.

  4. Create a variable ans to store the sender with the current largest word count, initialized to an empty string.

  5. Iterate over the items in the Counter object to find the sender with the largest word count. For each sender, two conditions are checked:

    • If the current word count (v) is greater than the word count of the sender stored in ans, or
    • If the current word count (v) is equal to the word count of the sender stored in ans, but the name of the current sender comes later lexicographically (ans < sender),

    then we update ans to the current sender.

  6. After examining all senders, ans contains the name of the sender with the largest word count. If there are multiple senders with the same word count, ans will be the lexicographically largest one among them, due to the way we perform the check in the previous step.

  7. Finally, we return ans.

The clever use of the Counter class along with the zip function allows for a clean and efficient solution. The overall time complexity is O(N) where N represents the total number of words across all messages because we iterate through every word at least once. Handling the lexicographical comparison during the iteration over the Counter items ensures that we only need one pass to find the sender who satisfies both conditions in the problem statement.

Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:

Which of these properties could exist for a graph but not a tree?

Example Walkthrough

Let's use a small example to illustrate the solution approach with sample messages and senders.

Suppose we have the following inputs:

messages = ["Hello world", "Hi", "Good morning", "Good night"] senders = ["Alice", "Alice", "Bob", "Bob"]

We're tasked with determining who has the highest word count of all the messages they sent. Following the solution approach:

  1. Initialize a Counter object cnt.
  2. Zip messages and senders and iterate over them.
  3. For each message-sender pair, use msg.count(' ') + 1 to count the words and update the sender's total count in cnt.
  4. Initialize a variable ans to store the sender with the highest word count so far.

We start by zipping and iterating over the pairs:

  • First iteration: The message is "Hello world", and the sender is Alice. The word count for this message is 2 ("Hello world".count(' ') + 1). Update Alice's count in cnt: cnt[Alice] = 2.

Current cnt status: {"Alice": 2}

  • Second iteration: The message is "Hi", and the sender is also Alice. The word count for this message is 1 ("Hi".count(' ') + 1). Update Alice's count in cnt: cnt[Alice] = 2 + 1 = 3.

Current cnt status: {"Alice": 3}

  • Third iteration: The message is "Good morning", and the sender is Bob. The word count is 2 ("Good morning".count(' ') + 1). Update Bob's count in cnt: cnt[Bob] = 2.

Current cnt status: {"Alice": 3, "Bob": 2}

  • Fourth iteration: The message is "Good night", and the sender is Bob again. The word count is 2 ("Good night".count(' ') + 1). Update Bob's count in cnt: cnt[Bob] = 2 + 2 = 4.

Final cnt status: {"Alice": 3, "Bob": 4}

  1. Iterate over cnt to find the sender with the largest word count. We compare values and update ans accordingly:
  • Since Bob has a higher word count (4) than Alice (3), we set ans = "Bob".
  1. After checking all the entries in cnt, we find that Bob has the largest word count.

  2. Return ans, which is "Bob".

Through this example, we have walked through the steps outlined in the solution approach, leading us to conclude that Bob is the sender with the highest word count from the given messages.

Solution Implementation

1from collections import Counter
2
3class Solution:
4    def largestWordCount(self, messages: List[str], senders: List[str]) -> str:
5        # Create a counter for tracking number of words sent by each sender
6        word_count_by_sender = Counter()
7
8        # Iterate over the messages and their corresponding senders simultaneously
9        for message, sender in zip(messages, senders):
10            # Count the number of words in the message and update the sender's count.
11            # Adding 1 because the number of words is one more than the number of spaces.
12            word_count_by_sender[sender] += message.count(' ') + 1
13
14        # Initialize a variable to keep track of the sender with the highest word count
15        top_sender = ''
16      
17        # Iterate over the senders in the counter to find the one with the highest word count
18        for sender, word_count in word_count_by_sender.items():
19            # Compare the current highest word count to this sender's count,
20            # or check alphabetical order if there's a tie on word count
21            if word_count_by_sender[top_sender] < word_count or \
22               (word_count_by_sender[top_sender] == word_count and top_sender < sender):
23                # Update the sender with the largest word count or lexicographically smaller sender on tie
24                top_sender = sender
25      
26        # Return the sender with the largest total word count
27        return top_sender
28
1class Solution {
2    public String largestWordCount(String[] messages, String[] senders) {
3        // Create a map to store the word count for each sender
4        Map<String, Integer> wordCountBySender = new HashMap<>();
5        int senderCount = senders.length;
6      
7        // Iterate over messages to count words and aggregate by sender
8        for (int i = 0; i < senderCount; ++i) {
9            // Start word count from 1 since each message has at least one word
10            int wordCount = 1;
11            // Count words in the message (each space signifies a new word)
12            for (int j = 0; j < messages[i].length(); ++j) {
13                if (messages[i].charAt(j) == ' ') {
14                    ++wordCount;
15                }
16            }
17            // Merge the word count with the existing count in the map for the sender
18            wordCountBySender.merge(senders[i], wordCount, Integer::sum);
19        }
20      
21        // Initial sender to compare with others using first sender's name
22        String senderWithMaxWords = senders[0];
23        // Iterate through the map to find the sender with the highest word count
24        for (var entry : wordCountBySender.entrySet()) {
25            String currentSender = entry.getKey();
26            // Compare word counts and sender names to find the sender with the maximum words
27            if (wordCountBySender.get(senderWithMaxWords) < entry.getValue()
28                || (wordCountBySender.get(senderWithMaxWords).equals(entry.getValue())
29                && senderWithMaxWords.compareTo(currentSender) < 0)) {
30                senderWithMaxWords = currentSender;
31            }
32        }
33        // Return the sender who has the maximum count of words
34        return senderWithMaxWords;
35    }
36}
37
1#include <vector>
2#include <string>
3#include <unordered_map>
4#include <algorithm>
5
6class Solution {
7public:
8    // Function to find the sender with the largest word count in the sent messages.
9    // If multiple senders have the same word count, the sender with the lexicographically largest name is returned.
10    string largestWordCount(vector<string>& messages, vector<string>& senders) {
11        unordered_map<string, int> wordCountBySender; // Create a map to store the word counts for each sender.
12        int totalSenders = senders.size(); // Total number of senders.
13      
14        // Calculate the word count for each message and aggregate it by sender.
15        for (int i = 0; i < totalSenders; ++i) {
16            // Count the words in the current message. Words are separated by spaces.
17            int wordCount = count(messages[i].begin(), messages[i].end(), ' ') + 1;
18            wordCountBySender[senders[i]] += wordCount; // Update the sender's total word count.
19        }
20      
21        string largestWordCountSender = senders[0]; // Initialize with the first sender.
22      
23        // Find the sender with the highest word count or lexicographically largest name on a tie.
24        for (const auto& [sender, wordCount] : wordCountBySender) {
25            if (wordCountBySender[largestWordCountSender] < wordCount || // Check for a higher word count.
26                (wordCountBySender[largestWordCountSender] == wordCount && largestWordCountSender < sender)) { // Check for a lexicographically larger name on a tie.
27                largestWordCountSender = sender;
28            }
29        }
30      
31        return largestWordCountSender; // Return the sender with the largest word count.
32    }
33};
34
1export const messageAnalysis = {
2  // Function to count the number of words in a message.
3  countWordsInMessage(message: string): number {
4    return message.split(' ').length; // Words are separated by spaces.
5  },
6
7  // Function to find the sender with the largest word count in the sent messages.
8  // If multiple senders have the same word count, the sender with the lexicographically largest name is returned.
9  largestWordCount(messages: string[], senders: string[]): string {
10    const wordCountBySender: { [sender: string]: number } = {}; // Map to store the word counts for each sender.
11
12    // Calculate the word count for each message and aggregate it by sender.
13    messages.forEach((message, index) => {
14      const wordCount = this.countWordsInMessage(message); // Get the word count for the current message.
15      const sender = senders[index]; // Corresponding sender for the message.
16    
17      if (wordCountBySender[sender]) {
18        wordCountBySender[sender] += wordCount; // Update the sender's total word count.
19      } else {
20        wordCountBySender[sender] = wordCount; // Initialize the sender's word count.
21      }
22    });
23
24    let largestWordCountSender = senders[0]; // Initialize with the first sender.
25
26    // Find the sender with the highest word count or lexicographically largest name on a tie.
27    Object.keys(wordCountBySender).forEach(sender => {
28      const wordCount = wordCountBySender[sender];
29      const isWordCountHigher = 
30        wordCountBySender[largestWordCountSender] < wordCount;
31      const isSenderNameGreater =
32        wordCountBySender[largestWordCountSender] === wordCount &&
33        largestWordCountSender < sender;
34
35      if (isWordCountHigher || isSenderNameGreater) {
36        largestWordCountSender = sender;
37      }
38    });
39
40    return largestWordCountSender; // Return the sender with the largest word count.
41  }
42};
43
Not Sure What to Study? Take the 2-min Quiz:

A heap is a ...?

Time and Space Complexity

Time Complexity

The given code performs the following operations:

  • It zips and iterates through messages and senders, which takes O(N) time, where N is the number of messages/senders.
  • Within the iteration, for each message, it counts the number of spaces and adds one to get the number of words in the message. If we assume M is the maximum length of a message, this operation takes O(M) for each message. So, for all messages, it is O(N*M).
  • After populating the counter, it iterates over each unique sender to find the sender with the highest word count or the lexicographically greatest sender in case of a tie. Let S be the number of unique senders, then this operation takes O(S) time. Note that in the worst case, S can be equal to N if all messages are from different senders.

To summarize, the overall time complexity is O(N*M + S), which simplifies to O(N*M) since M can vary independently of N and S.

Space Complexity

The space complexity consists of the following:

  • The counter hash map cnt that may at most contain S unique senders, where S can be at most N. Therefore, it is O(S). In the worst case, this is O(N).
  • The temporary variables used for iteration and comparison use constant space, which is O(1).

Combining these, the overall space complexity is O(S) which, in the worst case, simplifies to O(N).

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

Fast Track Your Learning with Our Quick Skills Quiz:

Which algorithm is best for finding the shortest distance between two points in an unweighted graph?


Recommended Readings


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫