Facebook Pixel

2284. Sender With Largest Word Count

MediumArrayHash TableStringCounting
Leetcode Link

Problem Description

You are given a chat log containing n messages. The chat log is represented by two arrays:

  • messages: An array where messages[i] contains the text of the i-th message
  • senders: An array where senders[i] contains the name of the person who sent the i-th message

Each message consists of words separated by single spaces, with no extra spaces at the beginning or end. Your task is to find which sender has sent the most total words across all their messages.

Key points to understand:

  • A sender can send multiple messages
  • The word count for a sender is the total number of words across ALL messages they sent
  • To count words in a message, count the number of space-separated tokens (e.g., "hello world" has 2 words)

The goal is to return the name of the sender with the largest total word count. If multiple senders have the same highest word count, return the name that comes last lexicographically (the "largest" name when sorted alphabetically).

Important notes about lexicographical comparison:

  • Uppercase letters come before lowercase letters (e.g., 'Z' < 'a')
  • Names are case-sensitive (e.g., "Alice" and "alice" are treated as different senders)

For example, if Alice sends "hello world" (2 words) and "good morning everyone" (3 words), her total word count is 5. If Bob sends "hi there how are you" (5 words), and both have the highest count, you would return "Bob" since "Bob" > "Alice" lexicographically.

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

Intuition

The core challenge is to track the total word count for each sender across potentially multiple messages. Since a sender can appear multiple times in the senders array with different messages, we need a way to accumulate their total word count.

A hash table (or dictionary) naturally fits this need - we can use the sender's name as the key and maintain a running total of their word count as the value. Each time we encounter a message from a sender, we add the word count of that message to their existing total.

To count words in a message, we observe that words are separated by single spaces. If a message has n spaces, it contains n + 1 words. For example, "hello world" has 1 space and 2 words. This gives us an efficient way to count words: message.count(" ") + 1.

Once we've built our word count mapping, we need to find the sender with the maximum word count. We iterate through all sender-count pairs and keep track of the best candidate so far. When comparing candidates, we first check the word count. If two senders have the same count, we choose the one with the lexicographically larger name using string comparison (ans < k).

The algorithm essentially breaks down into two phases:

  1. Aggregation phase: Process all messages to build a complete word count for each sender
  2. Selection phase: Find the sender that satisfies our criteria (max word count, with lexicographic tiebreaker)

This approach is efficient because we only need to process each message once and then make a single pass through the aggregated data to find our answer.

Solution Approach

The solution uses a hash table to track word counts and then finds the sender with the maximum count through enumeration.

Step 1: Initialize the Counter

cnt = Counter()

We use Python's Counter (a specialized dictionary) to store the word count for each sender. The keys will be sender names, and values will be their total word counts.

Step 2: Build Word Count Mapping

for message, sender in zip(messages, senders):
    cnt[sender] += message.count(" ") + 1

We iterate through messages and senders simultaneously using zip(). For each message:

  • Count the number of spaces using message.count(" ")
  • Add 1 to get the total word count (since n spaces means n+1 words)
  • Add this count to the sender's running total in the hash table

If a sender appears multiple times, their counts accumulate automatically.

Step 3: Initialize the Answer

ans = senders[0]

We start by assuming the first sender is our answer. This gives us a baseline for comparison.

Step 4: Find the Optimal Sender

for k, v in cnt.items():
    if cnt[ans] < v or (cnt[ans] == v and ans < k):
        ans = k

We enumerate through all sender-count pairs in the hash table. For each sender k with word count v:

  • Update ans to k if:
    • The current sender has more words than our current answer (cnt[ans] < v), OR
    • They have the same word count BUT their name is lexicographically larger (cnt[ans] == v and ans < k)

The condition ans < k performs lexicographic comparison where the "smaller" string comes first alphabetically, so we want to update when we find a "larger" name.

Step 5: Return the Result

return ans

After checking all senders, ans contains the name of the sender with the most words (with lexicographic tiebreaking).

Time Complexity: O(n * m) where n is the number of messages and m is the average message length (for counting spaces)
Space Complexity: O(k) where k is the number of unique senders

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's walk through a small example to illustrate the solution approach.

Input:

  • messages = ["Hi there", "Good morning everyone", "Hello", "How are you today"]
  • senders = ["Alice", "Bob", "Alice", "Bob"]

Step 1: Initialize the Counter We start with an empty counter: cnt = {}

Step 2: Build Word Count Mapping

Processing each message-sender pair:

  • Message 0: "Hi there" from Alice

    • Word count: 1 space + 1 = 2 words
    • cnt = {"Alice": 2}
  • Message 1: "Good morning everyone" from Bob

    • Word count: 2 spaces + 1 = 3 words
    • cnt = {"Alice": 2, "Bob": 3}
  • Message 2: "Hello" from Alice

    • Word count: 0 spaces + 1 = 1 word
    • Alice already exists, so we add to her total
    • cnt = {"Alice": 3, "Bob": 3}
  • Message 3: "How are you today" from Bob

    • Word count: 3 spaces + 1 = 4 words
    • Bob already exists, so we add to his total
    • cnt = {"Alice": 3, "Bob": 7}

Step 3: Initialize the Answer Start with the first sender: ans = "Alice"

Step 4: Find the Optimal Sender

Iterate through the counter:

  • Check Alice (3 words): She's already our answer, skip
  • Check Bob (7 words):
    • Compare: cnt["Alice"] = 3 < 7? Yes!
    • Update: ans = "Bob"

Step 5: Return the Result Return "Bob" as the sender with the most words (7 total words)


Another example with lexicographic tiebreaking:

If we had:

  • messages = ["One two three", "Four five six"]
  • senders = ["Charlie", "Alice"]

After processing:

  • cnt = {"Charlie": 3, "Alice": 3}

When finding the optimal sender:

  • Start with ans = "Charlie"
  • Check Alice (3 words): cnt["Charlie"] = 3 equals 3, and "Charlie" > "Alice" lexicographically
  • Keep ans = "Charlie" (no update needed)

Return "Charlie" because when tied at 3 words each, "Charlie" comes after "Alice" alphabetically.

Solution Implementation

1from typing import List
2from collections import Counter
3
4class Solution:
5    def largestWordCount(self, messages: List[str], senders: List[str]) -> str:
6        # Dictionary to store word count for each sender
7        word_count = Counter()
8      
9        # Calculate word count for each sender
10        for message, sender in zip(messages, senders):
11            # Count words by counting spaces and adding 1
12            word_count[sender] += message.count(" ") + 1
13      
14        # Initialize result with the first sender
15        result = senders[0]
16      
17        # Find sender with maximum word count
18        # If tied, return the lexicographically larger name
19        for sender, count in word_count.items():
20            if word_count[result] < count or (word_count[result] == count and result < sender):
21                result = sender
22      
23        return result
24
1class Solution {
2    public String largestWordCount(String[] messages, String[] senders) {
3        // Map to store the total word count for each sender
4        Map<String, Integer> wordCountMap = new HashMap<>(senders.length);
5      
6        // Calculate word count for each message and accumulate by sender
7        for (int i = 0; i < messages.length; i++) {
8            // Count words in current message (words = spaces + 1)
9            int wordCount = 1;
10            for (int j = 0; j < messages[i].length(); j++) {
11                if (messages[i].charAt(j) == ' ') {
12                    wordCount++;
13                }
14            }
15          
16            // Add word count to sender's total using merge method
17            // If sender exists, add to existing count; otherwise, set initial count
18            wordCountMap.merge(senders[i], wordCount, Integer::sum);
19        }
20      
21        // Initialize result with the first sender
22        String result = senders[0];
23      
24        // Find sender with maximum word count
25        // If tied, choose lexicographically larger name
26        for (Map.Entry<String, Integer> entry : wordCountMap.entrySet()) {
27            String currentSender = entry.getKey();
28            int currentWordCount = entry.getValue();
29          
30            // Update result if current sender has more words
31            // OR has same word count but lexicographically larger name
32            if (wordCountMap.get(result) < currentWordCount || 
33                (wordCountMap.get(result) == currentWordCount && result.compareTo(currentSender) < 0)) {
34                result = currentSender;
35            }
36        }
37      
38        return result;
39    }
40}
41
1class Solution {
2public:
3    string largestWordCount(vector<string>& messages, vector<string>& senders) {
4        // Map to store word count for each sender
5        unordered_map<string, int> wordCountMap;
6      
7        // Calculate word count for each message and accumulate by sender
8        for (int i = 0; i < messages.size(); ++i) {
9            // Count words by counting spaces and adding 1
10            int wordCount = count(messages[i].begin(), messages[i].end(), ' ') + 1;
11            wordCountMap[senders[i]] += wordCount;
12        }
13      
14        // Initialize result with the first sender
15        string result = senders[0];
16      
17        // Find sender with maximum word count
18        // If tied, choose lexicographically larger name
19        for (auto& [sender, count] : wordCountMap) {
20            if (wordCountMap[result] < count || 
21                (wordCountMap[result] == count && result < sender)) {
22                result = sender;
23            }
24        }
25      
26        return result;
27    }
28};
29
1/**
2 * Finds the sender with the largest total word count across all their messages.
3 * If there's a tie, returns the lexicographically larger sender name.
4 * 
5 * @param messages - Array of messages where each message contains space-separated words
6 * @param senders - Array of sender names corresponding to each message
7 * @returns The name of the sender with the most total words
8 */
9function largestWordCount(messages: string[], senders: string[]): string {
10    // Map to store the total word count for each sender
11    const wordCountBySender: Record<string, number> = {};
12
13    // Calculate total word count for each sender
14    for (let i = 0; i < messages.length; i++) {
15        // Count words in current message by splitting on spaces
16        const wordCount: number = messages[i].split(' ').length;
17      
18        // Add word count to sender's total (initialize to 0 if first occurrence)
19        wordCountBySender[senders[i]] = (wordCountBySender[senders[i]] || 0) + wordCount;
20    }
21
22    // Initialize result with the first sender
23    let result: string = senders[0];
24  
25    // Find sender with maximum word count
26    for (const sender in wordCountBySender) {
27        // Update result if current sender has more words,
28        // or same word count but lexicographically larger name
29        if (wordCountBySender[result] < wordCountBySender[sender] || 
30            (wordCountBySender[result] === wordCountBySender[sender] && result < sender)) {
31            result = sender;
32        }
33    }
34
35    return result;
36}
37

Time and Space Complexity

The time complexity is O(n + L), where n is the number of messages and L is the total length of all messages.

  • The zip(messages, senders) operation iterates through n messages: O(n)
  • For each message, message.count(" ") scans through the entire message to count spaces, which takes time proportional to the length of that message
  • The total time for counting spaces across all messages is O(L) where L is the sum of lengths of all messages
  • The second loop iterates through the Counter dictionary which has at most n entries (one per unique sender): O(n)
  • Overall time complexity: O(n + L)

The space complexity is O(n), where n is the number of messages.

  • The Counter object cnt stores at most n key-value pairs (in the worst case, each message has a unique sender)
  • The variable ans stores a single string reference: O(1)
  • The space used by the Counter is dominant: O(n)

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

Common Pitfalls

1. Incorrect Word Counting Logic

Pitfall: Attempting to count words using split() or other methods instead of counting spaces.

# WRONG - This creates unnecessary overhead
word_count[sender] += len(message.split())

While split() works correctly, it's less efficient as it creates a list of all words. Counting spaces and adding 1 is more memory-efficient.

2. Misunderstanding Lexicographical Comparison

Pitfall: Incorrectly implementing the tiebreaker logic when multiple senders have the same word count.

# WRONG - Returns lexicographically smaller name
if word_count[result] < count or (word_count[result] == count and result > sender):
    result = sender

The problem asks for the lexicographically larger name when there's a tie. Remember that in Python, "Bob" > "Alice" evaluates to True, and we want "Bob" in this case.

3. Not Handling Case Sensitivity

Pitfall: Converting names to lowercase or uppercase for comparison.

# WRONG - Loses case sensitivity
word_count[sender.lower()] += message.count(" ") + 1

The problem explicitly states that names are case-sensitive. "Alice" and "alice" should be treated as different senders.

4. Incorrect Initialization of Result

Pitfall: Not initializing the result variable properly or using an empty string.

# WRONG - What if this sender doesn't exist in word_count?
result = ""
# or
result = None

Always initialize with a valid sender name (like senders[0]) to ensure proper comparison in the loop.

5. Edge Case with Empty Messages

Pitfall: Not considering that a message might be a single word with no spaces.

# This actually works correctly!
message.count(" ") + 1  # For "hello", returns 0 + 1 = 1 word

The space-counting approach handles single-word messages correctly, but developers might overthink and add unnecessary edge case handling.

Corrected Solution Approach:

def largestWordCount(self, messages: List[str], senders: List[str]) -> str:
    word_count = Counter()
  
    # Build word counts correctly
    for message, sender in zip(messages, senders):
        word_count[sender] += message.count(" ") + 1
  
    # Find maximum with correct lexicographical tiebreaking
    max_sender = None
    max_count = 0
  
    for sender, count in word_count.items():
        if count > max_count or (count == max_count and (max_sender is None or sender > max_sender)):
            max_sender = sender
            max_count = count
  
    return max_sender
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following is a min heap?


Recommended Readings

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

Load More