Python 1002.py
class Solution:
def commonChars(self, words: List[str]) -> List[str]:
all = None
for w in words:
freq = defaultdict(int)
for c in w:
freq[c] += 1
if all:
for f in all:
all[f] = min(all[f], freq[f])
else:
all = freq
for a in all:
for _ in range(all[a]):
yield a
Difficulty
and has an acceptance rate of about 75% (400k+ submissions).
Typical patterns to solve this problem:
- Array
- Hash Table
- String
AI Disclaimer
However, two years later I set up this website and used various AI tools to generate most of the walkthrough / explanations below.
See this dedicated page for more details.
Code Walkthrough
Overview
The provided implementation computes the characters that appear in all strings of the input list words. It returns each common character as many times as it occurs in the smallest frequency across the words. The algorithm proceeds by building a frequency map for each word, intersecting these maps, and finally yielding the resulting characters.
Key Steps
Initialization
all = None
A variable named all is created to hold the cumulative intersection of character frequencies. It starts as None to indicate that no frequency map has been processed yet.
Frequency Counting for a Single Word
freq = defaultdict(int)
for c in w:
freq[c] += 1
For each word w in the input, a defaultdict of integers records how many times each character appears. This step isolates the per-word character distribution before it is merged with the global intersection.
Intersection Update
if all:
for f in all:
all[f] = min(all[f], freq[f])
else:
all = freq
When processing the first word, all is simply assigned the freshly built freq. For subsequent words, the algorithm iterates over the keys already present in all and reduces each count to the minimum of the existing value and the count in the current word’s freq. Characters absent from the current word automatically receive a count of zero because freq[f] defaults to 0, effectively removing them from the intersection.
Result Generation
for a in all:
for _ in range(all[a]):
yield a
After all words have been examined, the final frequency map all contains the exact number of times each character should appear in the output. The nested loops emit each character a repeatedly according to its stored count, using a generator (yield) to produce the result lazily.
Edge Cases Handled
The algorithm gracefully manages words that contain no overlapping characters. Because the intersection step uses min with a default of 0 for missing keys, any character that disappears in a later word will have its count reduced to 0. Consequently, the final generation loop will produce no output for that character, yielding an empty result when no common characters exist.
When the input list consists of a single word, the else branch assigns all directly to that word’s frequency map. The subsequent generation step then yields each character exactly as many times as it appears in the sole word, satisfying the definition of “common to all words” in the trivial single-element case.
If a word contains repeated characters, the frequency counting step records the exact multiplicity, and the intersection step preserves the smallest multiplicity across all words. This ensures that characters are emitted the correct number of times, even when some words have higher counts than others.
Performance Optimizations
The implementation avoids constructing intermediate full lists of characters. By using a defaultdict for frequency counting, it achieves constant-time updates for each character occurrence. The intersection step iterates only over keys already present in the cumulative map, which limits work to characters that have survived previous intersections, reducing unnecessary comparisons.
The final output is produced via a generator (yield). This lazy approach means that the caller can consume the result incrementally without allocating a complete list in memory, which is advantageous when the result set is large. The combination of in-place frequency updates and lazy emission minimizes both time spent on redundant data structures and memory footprint.
ELI5 - Simplified Explanation
Imagine a group of friends, each carrying a bag filled with letter blocks. The goal is to see which letters appear in every single friend’s bag, and to keep only as many of each letter as everyone has in common.
The code starts by looking at the first friend’s bag and keeping track of how many of each letter they have. Then, it looks at the next friend’s bag. If a friend has five blocks of a certain letter but someone else only has two, the code decides to keep just those two, because that is the smallest amount everyone can share. It repeats this process for every bag in the group, shrinking the collection down until only the shared letters remain.
After checking every bag, the code looks at the final list of shared letters and counts them up. For every letter it saved, it pulls that many blocks out of the imaginary pile to show what was found.
Code Quality
Complexity Analysis
Other Solutions
Obviously, I did that after I solved the problem myself.
In general, AlgoMonster and WalkCCC solutions are high-quality and always worth a look.
I haven't checked most of the other solutions, but they might be helpful as well.
And of course, the LeetCode forum is always a good place (or even the best) to look for alternative approaches.
(click on a column header to sort the table)
| author | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| author | solutions | |||||||||
| www | AlgoMonster | C++ | Python | Java | TS | |||||
| WalkCCC | C++ | Python | Java | |||||||
| leetcode.ca | C++ | Python | Java | Go | TS | |||||
| LeetcodeHelp | C++ | Python | Java | |||||||
| Progiez | C++ | Python | Java | |||||||
| github | BigEggStudy | C# | ||||||||
| black-shadows | C++ | |||||||||
| chandansgowda | Python | |||||||||
| charles-wangkai | Java | |||||||||
| DimitrisJim | C | |||||||||
| everthis | JS | |||||||||
| fartem | Ruby | |||||||||
| fsq | C++ | |||||||||
| Garvit244 | Python | |||||||||
| halfrost | Go | |||||||||
| JoshCrozier | JS | |||||||||
| keineahnung2345 | C++ | |||||||||
| Krazune | C++ | |||||||||
| lkwq007 | Python | |||||||||
| lzl124631x | C++ | |||||||||
| shreymehul | C# | |||||||||
| suxrobGM | C# | |||||||||
| varunu28 | Java | |||||||||
| YuriSpiridonov | Python | |||||||||
| AlgoMonster | C++, Python, Java, TypeScript | |||||||||
| WalkCCC | C++, Python, Java | |||||||||
| leetcode.ca | C++, Go, Java, Python, TypeScript | |||||||||
| LeetcodeHelp | C++, Java, Python | |||||||||
| Progiez | C++, Java, Python | |||||||||
| BigEggStudy | C# | |||||||||
| black-shadows | C++, C++ | |||||||||
| chandansgowda | Python | |||||||||
| charles-wangkai | Java | |||||||||
| DimitrisJim | C | |||||||||
| everthis | JavaScript | |||||||||
| fartem | Ruby | |||||||||
| fsq | C++ | |||||||||
| Garvit244 | Python | |||||||||
| halfrost | Go | |||||||||
| JoshCrozier | JavaScript | |||||||||
| keineahnung2345 | C++ | |||||||||
| Krazune | C++ | |||||||||
| lkwq007 | Python | |||||||||
| lzl124631x | C++ | |||||||||
| shreymehul | C# | |||||||||
| suxrobGM | C# | |||||||||
| varunu28 | Java | |||||||||
| YuriSpiridonov | Python |
Videos
Similar Problems
- problem 350: Intersection of Two Arrays II
Solved On The Same Day
- problem 1025: Divisor Game
Extra
- Tech Stack - my development environment and tools
- AI - how AI helped me create this website (NOT solving the problems)
- Progress - progression over time
- Quality - code quality