LeetCode 2085 - Count Common Words With One Occurrence
This problem gives us two arrays of strings, words1 and words2. Our task is to count how many strings appear exactly once in both arrays. The key detail is that a word only qualifies if: 1. It appears exactly one time in words1 2.
Difficulty: 🟢 Easy
Topics: Array, Hash Table, String, Counting
Solution
LeetCode 2085, Count Common Words With One Occurrence
Problem Understanding
This problem gives us two arrays of strings, words1 and words2. Our task is to count how many strings appear exactly once in both arrays.
The key detail is that a word only qualifies if:
- It appears exactly one time in
words1 - It appears exactly one time in
words2
If either array contains the word multiple times, then that word does not count.
For example, consider:
words1 = ["leetcode","is","amazing","as","is"]
words2 = ["amazing","leetcode","is"]
The word "leetcode" appears once in both arrays, so it counts.
The word "amazing" also appears once in both arrays, so it counts.
The word "is" appears twice in words1, so even though it appears in words2, it does not count.
The input arrays can each contain up to 1000 words, and each word can have up to 30 lowercase English letters. These constraints are small enough that many approaches would work in practice, but the problem is fundamentally about counting frequencies efficiently.
The important observation is that the problem is not asking whether words merely exist in both arrays. It specifically requires frequency counts. That means we must track how many times each word appears in each array.
Several edge cases are important:
- A word may exist in one array but not the other.
- A word may appear multiple times in one array and once in the other.
- All words may be distinct.
- All words may be repeated many times.
- The arrays may contain only one element each.
A naive implementation that repeatedly scans arrays for counts could become unnecessarily slow, especially if repeated for every word.
Approaches
Brute Force Approach
The brute force solution checks every unique word and manually counts how many times it appears in both arrays.
One way to do this is:
- Iterate through every word in
words1 - Count its occurrences in
words1 - Count its occurrences in
words2 - If both counts equal 1, increment the answer
The counting operation itself requires scanning the arrays repeatedly. Since each lookup may take linear time, the total complexity becomes quadratic.
This approach is correct because it explicitly verifies the exact frequency conditions required by the problem. However, it is inefficient because the same counting work is repeated many times.
Optimal Approach
The better solution uses hash maps, also called dictionaries in Python or maps in Go, to store word frequencies.
The key insight is that frequency counting is exactly what hash maps are designed for. Instead of repeatedly scanning arrays, we can preprocess each array once and store how many times each word appears.
After building the two frequency maps:
- Check every word in the first map
- If its frequency is 1 in both maps, count it
This reduces repeated work and gives an efficient linear-time solution.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n × m + n²) | O(1) | Repeatedly scans arrays to count occurrences |
| Optimal | O(n + m) | O(n + m) | Uses hash maps for frequency counting |
Here, n is the length of words1 and m is the length of words2.
Algorithm Walkthrough
- Create a hash map for
words1.
This map stores each word as a key and its number of occurrences as the value. For example:
{"leetcode": 1, "is": 2, "amazing": 1, "as": 1}
Using a hash map allows constant average-time frequency updates.
2. Create another hash map for words2.
This performs the same frequency counting for the second array.
3. Initialize a variable called common_count to 0.
This variable stores the number of valid words that appear exactly once in both arrays. 4. Iterate through the keys in the first frequency map.
For each word:
- Check whether its count in
words1equals 1 - Check whether its count in
words2also equals 1
If both conditions are true, increment common_count.
5. Return common_count.
After all words are processed, the variable contains the final answer.
Why it works
The algorithm works because the frequency maps store the exact number of occurrences for every word in both arrays. A word satisfies the problem condition if and only if its stored frequency equals 1 in both maps. Since every word frequency is computed correctly during preprocessing, the final count is guaranteed to be correct.
Python Solution
from typing import List
from collections import Counter
class Solution:
def countWords(self, words1: List[str], words2: List[str]) -> int:
frequency1 = Counter(words1)
frequency2 = Counter(words2)
common_count = 0
for word in frequency1:
if frequency1[word] == 1 and frequency2[word] == 1:
common_count += 1
return common_count
The implementation begins by importing Counter from Python's collections module. Counter automatically builds a frequency map from a list of words.
frequency1 stores the counts for words1, while frequency2 stores the counts for words2.
The variable common_count keeps track of how many valid words have been found.
The loop iterates through every unique word in frequency1. For each word, the code checks whether the count equals 1 in both maps. If so, the answer is incremented.
Finally, the method returns the total count.
This implementation directly follows the algorithm described earlier and uses Python's built-in data structures to keep the code concise and efficient.
Go Solution
func countWords(words1 []string, words2 []string) int {
frequency1 := make(map[string]int)
frequency2 := make(map[string]int)
for _, word := range words1 {
frequency1[word]++
}
for _, word := range words2 {
frequency2[word]++
}
commonCount := 0
for word, count := range frequency1 {
if count == 1 && frequency2[word] == 1 {
commonCount++
}
}
return commonCount
}
The Go implementation uses map[string]int to store word frequencies.
Unlike Python's Counter, Go does not provide automatic frequency counting utilities in the standard library, so the counts are built manually using loops.
Looking up a nonexistent key in a Go map automatically returns the zero value for the type, which is 0 for integers. This makes the condition:
frequency2[word] == 1
safe even when the word does not exist in frequency2.
No special handling is required for empty slices because the loops naturally handle them correctly.
Worked Examples
Example 1
words1 = ["leetcode","is","amazing","as","is"]
words2 = ["amazing","leetcode","is"]
Step 1, Build frequency map for words1
| Word | Frequency |
|---|---|
| leetcode | 1 |
| is | 2 |
| amazing | 1 |
| as | 1 |
Step 2, Build frequency map for words2
| Word | Frequency |
|---|---|
| amazing | 1 |
| leetcode | 1 |
| is | 1 |
Step 3, Check words
| Word | Count in words1 | Count in words2 | Valid? |
|---|---|---|---|
| leetcode | 1 | 1 | Yes |
| is | 2 | 1 | No |
| amazing | 1 | 1 | Yes |
| as | 1 | 0 | No |
Final answer:
2
Example 2
words1 = ["b","bb","bbb"]
words2 = ["a","aa","aaa"]
Frequency maps
words1
| Word | Frequency |
|---|---|
| b | 1 |
| bb | 1 |
| bbb | 1 |
words2
| Word | Frequency |
|---|---|
| a | 1 |
| aa | 1 |
| aaa | 1 |
No words exist in both arrays.
Final answer:
0
Example 3
words1 = ["a","ab"]
words2 = ["a","a","a","ab"]
Frequency maps
words1
| Word | Frequency |
|---|---|
| a | 1 |
| ab | 1 |
words2
| Word | Frequency |
|---|---|
| a | 3 |
| ab | 1 |
Validation
| Word | Count in words1 | Count in words2 | Valid? |
|---|---|---|---|
| a | 1 | 3 | No |
| ab | 1 | 1 | Yes |
Final answer:
1
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n + m) | Each array is traversed once to build frequency maps |
| Space | O(n + m) | Hash maps store frequencies for unique words |
The algorithm performs a single pass through each array to build the frequency maps. Hash map insertions and lookups take constant average time, so the total running time is linear in the size of the input.
The space complexity is also linear because, in the worst case, every word is unique and must be stored in the maps.
Test Cases
from typing import List
class Solution:
def countWords(self, words1: List[str], words2: List[str]) -> int:
from collections import Counter
frequency1 = Counter(words1)
frequency2 = Counter(words2)
common_count = 0
for word in frequency1:
if frequency1[word] == 1 and frequency2[word] == 1:
common_count += 1
return common_count
solution = Solution()
assert solution.countWords(
["leetcode", "is", "amazing", "as", "is"],
["amazing", "leetcode", "is"]
) == 2 # provided example 1
assert solution.countWords(
["b", "bb", "bbb"],
["a", "aa", "aaa"]
) == 0 # provided example 2
assert solution.countWords(
["a", "ab"],
["a", "a", "a", "ab"]
) == 1 # provided example 3
assert solution.countWords(
["a"],
["a"]
) == 1 # smallest valid matching case
assert solution.countWords(
["a"],
["b"]
) == 0 # single elements do not match
assert solution.countWords(
["x", "x"],
["x"]
) == 0 # repeated in first array
assert solution.countWords(
["x"],
["x", "x"]
) == 0 # repeated in second array
assert solution.countWords(
["x", "x"],
["x", "x"]
) == 0 # repeated in both arrays
assert solution.countWords(
["a", "b", "c"],
["a", "b", "c"]
) == 3 # all words unique and shared
assert solution.countWords(
["apple", "banana", "apple", "orange"],
["banana", "orange", "orange"]
) == 1 # only banana appears once in both
assert solution.countWords(
["same", "same", "same"],
["same", "same"]
) == 0 # no word appears exactly once
assert solution.countWords(
["unique"],
["unique", "extra"]
) == 1 # one valid shared word
| Test | Why |
|---|---|
| Example 1 | Validates normal mixed frequencies |
| Example 2 | Validates no shared words |
| Example 3 | Validates repeated frequency rejection |
["a"], ["a"] |
Smallest positive case |
["a"], ["b"] |
Smallest negative case |
| Repeated in first array | Ensures duplicates invalidate result |
| Repeated in second array | Ensures duplicates invalidate result |
| Repeated in both arrays | Ensures duplicates never count |
| All unique and shared | Validates multiple successful matches |
| Mixed duplicate scenario | Tests selective counting |
| All repeated | Ensures answer can be zero |
| One shared unique word | Tests partial overlap |
Edge Cases
One important edge case occurs when a word appears multiple times in one array but exactly once in the other. A buggy implementation might incorrectly count such a word simply because it exists in both arrays. For example:
words1 = ["a", "a"]
words2 = ["a"]
The correct answer is 0, because "a" does not appear exactly once in words1. The frequency-map approach handles this correctly by explicitly checking both counts.
Another important edge case is when the arrays contain no overlapping words at all. For example:
words1 = ["x", "y"]
words2 = ["a", "b"]
A naive implementation that assumes matching keys exist could fail or produce incorrect results. In both Python and Go, missing keys safely evaluate to 0, so the implementation correctly returns 0.
A third edge case occurs when every word is unique and shared across both arrays. For example:
words1 = ["a", "b", "c"]
words2 = ["a", "b", "c"]
In this scenario, every word should count. The implementation correctly identifies all three words because each has frequency 1 in both maps.
A final edge case involves arrays of minimum size. The constraints allow arrays with only one element. For example:
words1 = ["hello"]
words2 = ["hello"]
The implementation handles this naturally because the frequency maps contain exactly one entry each, and the comparison logic remains unchanged.