884. Uncommon Words from Two Sentences
Problem Description
You are given two sentences s1
and s2
, where each sentence is a string containing words separated by single spaces. Each word in the sentences consists only of lowercase letters.
A word is considered uncommon if it meets the following criteria:
- It appears exactly once in one of the sentences
- It does not appear at all in the other sentence
Your task is to find all uncommon words from both sentences and return them as a list. The words can be returned in any order.
For example, if s1 = "this apple is sweet"
and s2 = "this apple is sour"
, the uncommon words would be ["sweet", "sour"]
because:
- "sweet" appears once in
s1
but not ins2
- "sour" appears once in
s2
but not ins1
- "this", "apple", and "is" appear in both sentences, so they are not uncommon
The solution uses a hash table (Counter) to count the occurrences of all words from both sentences combined. Since an uncommon word must appear exactly once total (once in one sentence and zero times in the other), we simply return all words that have a count of 1 in the combined frequency map.
Intuition
The key insight is to recognize what makes a word "uncommon" - it must appear exactly once in one sentence and not at all in the other.
If we think about this carefully, when we combine both sentences together, an uncommon word will have a total count of exactly 1. Why? Because:
- If a word appears once in
s1
and zero times ins2
, its total count is1 + 0 = 1
- If a word appears zero times in
s1
and once ins2
, its total count is0 + 1 = 1
Any other scenario would not qualify as uncommon:
- If a word appears in both sentences (even once each), its total count would be at least 2
- If a word appears multiple times in one sentence, its total count would be greater than 1
This observation leads us to a simple approach: instead of tracking which sentence each word comes from, we can just count all words from both sentences together. Any word with a count of exactly 1 is uncommon by definition.
This transforms the problem from "find words that appear once in one sentence but not in the other" to simply "find words that appear exactly once in total", which is much easier to implement using a frequency counter.
Solution Approach
The implementation uses a hash table to count word frequencies across both sentences:
-
Split and Count Words: First, we split both sentences into individual words using the
split()
method, which separates words by spaces. TheCounter
class from Python's collections module automatically creates a frequency map for each sentence. -
Combine Frequency Maps: We add the two
Counter
objects together using the+
operator. This combines the counts from both sentences. For example:- If "apple" appears once in
s1
and once ins2
, the combined count is 2 - If "sweet" appears once in
s1
and not ins2
, the combined count is 1 - If "sour" appears not in
s1
but once ins2
, the combined count is 1
- If "apple" appears once in
-
Filter Words with Count 1: We iterate through the combined counter using
cnt.items()
, which gives us(word, count)
pairs. Using a list comprehension, we filter and collect only those words where the countv
equals 1.
The complete solution in one line:
cnt = Counter(s1.split()) + Counter(s2.split()) return [s for s, v in cnt.items() if v == 1]
Time Complexity: O(n + m)
where n
and m
are the lengths of the two sentences, as we need to process each word once.
Space Complexity: O(n + m)
for storing the words in the hash table.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with a concrete example:
Input:
s1 = "apple orange banana"
s2 = "apple grape banana banana"
Step 1: Split and count words in each sentence
First, we split s1
into words and count them:
s1.split()
→["apple", "orange", "banana"]
Counter(s1.split())
→{"apple": 1, "orange": 1, "banana": 1}
Then split s2
into words and count them:
s2.split()
→["apple", "grape", "banana", "banana"]
Counter(s2.split())
→{"apple": 1, "grape": 1, "banana": 2}
Step 2: Combine the frequency maps
Add the two counters together:
{"apple": 1, "orange": 1, "banana": 1} + {"apple": 1, "grape": 1, "banana": 2} = {"apple": 2, "orange": 1, "banana": 3, "grape": 1}
The combined counter shows:
- "apple": appears 2 times total (once in each sentence)
- "orange": appears 1 time total (only in s1)
- "banana": appears 3 times total (once in s1, twice in s2)
- "grape": appears 1 time total (only in s2)
Step 3: Filter words with count = 1
Iterate through the combined counter and select words with count exactly 1:
- "apple": count is 2 → skip
- "orange": count is 1 → include ✓
- "banana": count is 3 → skip
- "grape": count is 1 → include ✓
Result: ["orange", "grape"]
These are the uncommon words because:
- "orange" appears exactly once in s1 and not at all in s2
- "grape" appears exactly once in s2 and not at all in s1
Solution Implementation
1from typing import List
2from collections import Counter
3
4class Solution:
5 def uncommonFromSentences(self, s1: str, s2: str) -> List[str]:
6 # Split both sentences into words and count occurrences
7 # Counter() creates a dictionary with word frequencies
8 word_count = Counter(s1.split()) + Counter(s2.split())
9
10 # Return words that appear exactly once across both sentences
11 # These are the "uncommon" words (appearing in only one sentence, once)
12 return [word for word, frequency in word_count.items() if frequency == 1]
13
1class Solution {
2 public String[] uncommonFromSentences(String s1, String s2) {
3 // Create a map to store word frequencies across both sentences
4 Map<String, Integer> wordFrequencyMap = new HashMap<>();
5
6 // Count occurrences of each word in the first sentence
7 for (String word : s1.split(" ")) {
8 // merge() adds 1 to existing count or initializes to 1 if word not present
9 wordFrequencyMap.merge(word, 1, Integer::sum);
10 }
11
12 // Count occurrences of each word in the second sentence
13 for (String word : s2.split(" ")) {
14 // merge() adds 1 to existing count or initializes to 1 if word not present
15 wordFrequencyMap.merge(word, 1, Integer::sum);
16 }
17
18 // Create a list to store uncommon words (words that appear exactly once)
19 List<String> uncommonWords = new ArrayList<>();
20
21 // Iterate through all entries in the frequency map
22 for (Map.Entry<String, Integer> entry : wordFrequencyMap.entrySet()) {
23 // Add word to result if it appears exactly once across both sentences
24 if (entry.getValue() == 1) {
25 uncommonWords.add(entry.getKey());
26 }
27 }
28
29 // Convert the list to array and return
30 return uncommonWords.toArray(new String[0]);
31 }
32}
33
1class Solution {
2public:
3 vector<string> uncommonFromSentences(string s1, string s2) {
4 // Hash map to store word frequencies across both sentences
5 unordered_map<string, int> wordCount;
6
7 // Lambda function to process a sentence and update word frequencies
8 auto processString = [&](string& sentence) {
9 stringstream stream(sentence);
10 string word;
11 // Extract each word from the sentence and increment its count
12 while (stream >> word) {
13 wordCount[move(word)]++;
14 }
15 };
16
17 // Process both input sentences
18 processString(s1);
19 processString(s2);
20
21 // Vector to store uncommon words (words appearing exactly once)
22 vector<string> result;
23
24 // Iterate through the word count map
25 for (auto& [word, frequency] : wordCount) {
26 // Add words that appear exactly once to the result
27 if (frequency == 1) {
28 result.emplace_back(word);
29 }
30 }
31
32 return result;
33 }
34};
35
1/**
2 * Finds uncommon words that appear exactly once across both sentences
3 * @param s1 - First sentence as a string
4 * @param s2 - Second sentence as a string
5 * @returns Array of uncommon words (words that appear exactly once in total)
6 */
7function uncommonFromSentences(s1: string, s2: string): string[] {
8 // Map to store word frequency count across both sentences
9 const wordFrequencyMap: Map<string, number> = new Map<string, number>();
10
11 // Split both sentences into words and combine them into a single array
12 const allWords: string[] = [...s1.split(' '), ...s2.split(' ')];
13
14 // Count frequency of each word
15 for (const word of allWords) {
16 const currentCount: number = wordFrequencyMap.get(word) || 0;
17 wordFrequencyMap.set(word, currentCount + 1);
18 }
19
20 // Array to store uncommon words (words with frequency = 1)
21 const uncommonWords: string[] = [];
22
23 // Iterate through the map and collect words that appear exactly once
24 for (const [word, frequency] of wordFrequencyMap.entries()) {
25 if (frequency === 1) {
26 uncommonWords.push(word);
27 }
28 }
29
30 return uncommonWords;
31}
32
Time and Space Complexity
Time Complexity: O(m + n)
The time complexity breaks down as follows:
s1.split()
takesO(m)
time to split strings1
into wordss2.split()
takesO(n)
time to split strings2
into words- Creating
Counter
fors1
words takesO(m)
time (iterating through all characters/words) - Creating
Counter
fors2
words takesO(n)
time (iterating through all characters/words) - Adding two Counter objects takes
O(m + n)
time in the worst case (combining all unique keys) - The list comprehension iterates through all items in the combined counter, which is at most
O(m + n)
unique words - Overall:
O(m) + O(n) + O(m + n) = O(m + n)
Space Complexity: O(m + n)
The space complexity breaks down as follows:
s1.split()
creates a list that takesO(m)
space (storing all characters froms1
as words)s2.split()
creates a list that takesO(n)
space (storing all characters froms2
as words)- The Counter objects store at most all unique words from both strings, requiring
O(m + n)
space - The result list stores words that appear exactly once, which is at most
O(m + n)
words - Overall:
O(m + n)
Where m
and n
are the lengths of strings s1
and s2
, respectively.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding the Problem Definition
A common mistake is thinking that uncommon words are those that appear in one sentence but not the other, regardless of frequency. This leads to incorrect solutions like:
# INCORRECT approach
def uncommonFromSentences(self, s1: str, s2: str) -> List[str]:
words1 = set(s1.split())
words2 = set(s2.split())
return list((words1 - words2) | (words2 - words1))
This fails when a word appears multiple times in one sentence. For example, with s1 = "apple apple"
and s2 = "banana"
, this would incorrectly return ["apple", "banana"]
when the answer should be just ["banana"]
(since "apple" appears twice).
Solution: Remember that uncommon words must appear exactly once in one sentence and not at all in the other. The combined count approach correctly handles this by ensuring only words with total count 1 are selected.
2. Incorrect String Splitting
Using incorrect splitting methods or forgetting to handle edge cases:
# INCORRECT - using wrong split delimiter words = s1.split(',') # Wrong delimiter # INCORRECT - manual splitting that doesn't handle multiple spaces words = s1.split(' ') # Fails with multiple consecutive spaces
Solution: Use split()
without arguments, which correctly handles any amount of whitespace:
words = s1.split() # Correctly splits on any whitespace
3. Not Handling Empty Strings or Edge Cases
Failing to consider empty or single-word sentences:
# Potential issue if not careful s1 = "" s2 = "hello world" # Need to ensure empty strings are handled properly
Solution: The split()
method on an empty string returns an empty list, and Counter()
handles empty lists correctly, so the provided solution naturally handles these edge cases.
4. Inefficient Duplicate Checking
Attempting to manually check for duplicates instead of using the Counter approach:
# INEFFICIENT approach
def uncommonFromSentences(self, s1: str, s2: str) -> List[str]:
result = []
words1 = s1.split()
words2 = s2.split()
for word in words1:
if words1.count(word) == 1 and word not in words2:
result.append(word)
for word in words2:
if words2.count(word) == 1 and word not in words1:
result.append(word)
return result
This has O(n²) time complexity due to the count()
method being called for each word.
Solution: Use the Counter approach which processes all words in O(n + m) time with a single pass through each sentence.
What does the following code do?
1def f(arr1, arr2):
2 i, j = 0, 0
3 new_arr = []
4 while i < len(arr1) and j < len(arr2):
5 if arr1[i] < arr2[j]:
6 new_arr.append(arr1[i])
7 i += 1
8 else:
9 new_arr.append(arr2[j])
10 j += 1
11 new_arr.extend(arr1[i:])
12 new_arr.extend(arr2[j:])
13 return new_arr
14
1public static List<Integer> f(int[] arr1, int[] arr2) {
2 int i = 0, j = 0;
3 List<Integer> newArr = new ArrayList<>();
4
5 while (i < arr1.length && j < arr2.length) {
6 if (arr1[i] < arr2[j]) {
7 newArr.add(arr1[i]);
8 i++;
9 } else {
10 newArr.add(arr2[j]);
11 j++;
12 }
13 }
14
15 while (i < arr1.length) {
16 newArr.add(arr1[i]);
17 i++;
18 }
19
20 while (j < arr2.length) {
21 newArr.add(arr2[j]);
22 j++;
23 }
24
25 return newArr;
26}
27
1function f(arr1, arr2) {
2 let i = 0, j = 0;
3 let newArr = [];
4
5 while (i < arr1.length && j < arr2.length) {
6 if (arr1[i] < arr2[j]) {
7 newArr.push(arr1[i]);
8 i++;
9 } else {
10 newArr.push(arr2[j]);
11 j++;
12 }
13 }
14
15 while (i < arr1.length) {
16 newArr.push(arr1[i]);
17 i++;
18 }
19
20 while (j < arr2.length) {
21 newArr.push(arr2[j]);
22 j++;
23 }
24
25 return newArr;
26}
27
Recommended Readings
Coding Interview 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
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 assets algo monster recursion jpg You first call Ben and ask
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!