2284. Sender With Largest Word Count
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:
- 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.
- 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.
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:
-
Initialize a
Counter
objectcnt
. This will hold our senders as keys and their word counts as values. -
Iterate over the zipped
messages
andsenders
arrays. Thezip
function is used to create pairs of message-sender which allows us to process them together. -
In the loop, increment the word count for each
sender
in theCounter
object by the number of words in their correspondingmsg
. Since words in a message are separated by spaces,msg.count(' ') + 1
gives us the number of words in the message. -
Create a variable
ans
to store the sender with the current largest word count, initialized to an empty string. -
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 inans
, or - If the current word count (
v
) is equal to the word count of the sender stored inans
, but the name of the current sender comes later lexicographically (ans < sender
),
then we update
ans
to the currentsender
. - If the current word count (
-
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. -
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.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample 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:
- Initialize a
Counter
objectcnt
. - Zip
messages
andsenders
and iterate over them. - For each message-sender pair, use
msg.count(' ') + 1
to count the words and update the sender's total count incnt
. - 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 incnt
: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 incnt
: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 incnt
: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 incnt
:cnt[Bob] = 2 + 2 = 4
.
Final cnt
status: {"Alice": 3, "Bob": 4}
- Iterate over
cnt
to find the sender with the largest word count. We compare values and updateans
accordingly:
- Since
Bob
has a higher word count (4) thanAlice
(3), we setans = "Bob"
.
-
After checking all the entries in
cnt
, we find thatBob
has the largest word count. -
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
Time and Space Complexity
Time Complexity
The given code performs the following operations:
- It zips and iterates through
messages
andsenders
, which takesO(N)
time, whereN
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 takesO(M)
for each message. So, for all messages, it isO(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 takesO(S)
time. Note that in the worst case,S
can be equal toN
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 containS
unique senders, whereS
can be at mostN
. Therefore, it isO(S)
. In the worst case, this isO(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.
In a binary min heap, the maximum element can be found in:
Recommended Readings
LeetCode 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 we
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 algomonster s3 us east 2 amazonaws com recursion jpg You first
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!