Python 0318.py
class Solution:
def maxProduct(self, words: List[str]) -> int:
def bitmask(word):
mask = 0
for c in w:
mask |= 1 << (ord(c) - ord('a'))
return mask
longest = defaultdict(set)
for w in words:
mask = bitmask(w)
longest[mask].add(w)
result = 0
for w in words:
me = len(w)
mask = bitmask(w)
for l in longest:
if mask & l == 0:
for o in longest[l]:
if o != w:
other = len(o)
result = max(result, me * other)
return result
Difficulty
and has an acceptance rate of about 61% (400k+ submissions).
Typical patterns to solve this problem:
- Array
- Bit Manipulation
- 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 defines a class Solution with a method maxProduct that receives a list of strings and returns the maximum product of the lengths of two distinct words that share no common characters. The algorithm relies on representing each word as a 26-bit integer mask, grouping words that share the same mask, and then testing mask pairs for disjointness.
Core Steps
Bitmask Construction
A helper function creates a compact integer representation of the characters present in a word.
def bitmask(word):
mask = 0
for c in w:
mask |= 1 << (ord(c) - ord('a'))
return mask
Each character c contributes a single bit at the position corresponding to its alphabetical index. The resulting mask enables constant-time set-intersection checks using bitwise &.
Grouping by Mask
The method builds a dictionary that maps each unique mask to the set of words that produce it.
longest = defaultdict(set)
for w in words:
mask = bitmask(w)
longest[mask].add(w)
Storing words in a set eliminates duplicate entries for the same mask while preserving all candidates that share the exact character composition.
Pairwise Evaluation
The algorithm iterates over every word, recomputes its mask, and then scans the dictionary keys to find masks that are completely disjoint. For each compatible mask, it examines the associated words, skips the current word itself, and updates the best product found.
result = 0
for w in words:
me = len(w)
mask = bitmask(w)
for l in longest:
if mask & l == 0:
for o in longest[l]:
if o != w:
other = len(o)
result = max(result, me * other)
The inner if mask & l == 0 test is the decisive filter that discards any pair sharing at least one character, allowing the algorithm to focus only on viable candidates.
Final Result
After all combinations have been examined, the method returns the highest product recorded.
return result
Edge Cases Handled
When the input list is empty or contains a single element, the outer loop runs but never finds a distinct partner, leaving result at its initial value of 0. This correctly reflects the fact that no valid pair exists.
If multiple words are identical or consist of the same set of letters, they map to the same mask and reside in the same set within longest. The inner check if o != w ensures that a word is never paired with itself, even when duplicates are present.
Words that contain every letter of the alphabet generate a mask with all 26 bits set. Such a mask will never satisfy the disjointness condition with any other mask, so the algorithm naturally excludes these cases without extra logic.
When a word is an empty string, its mask remains 0. The disjointness test mask & l == 0 will succeed for any other mask, allowing the empty word to be paired with any other word. The product will be zero because the length of the empty string is zero, which the algorithm correctly records as a possible (though not maximal) result.
Performance Optimizations
The primary optimization is the conversion of each word into a 26-bit integer. This representation compresses the character set into a single machine word, enabling the algorithm to test for shared characters with a single bitwise & operation, which is far faster than iterating over characters or using set intersections.
Grouping words by their mask reduces redundant work. Instead of comparing every word against every other word directly, the code compares each mask against the distinct set of masks stored as dictionary keys. This eliminates repeated mask calculations for words that share the same character composition.
Storing words in a set under each mask prevents duplicate entries from inflating the inner loop, ensuring that each distinct word is considered at most once per compatible mask.
The use of max to update the result avoids conditional branching beyond the necessary comparison, keeping the inner loop tight and cache-friendly.
Overall, the combination of bitwise masking, dictionary grouping, and set storage yields an approach that scales efficiently with the number of input words while keeping the inner operations lightweight.
ELI5 - Simplified Explanation
Imagine the alphabet is a line of buckets labeled from a to z. Every time a new word is picked up, it is checked for letters. If a word has the letter a, a little marble is dropped into the first bucket. If it has a b, a marble goes into the second bucket. By the end, each word turns into a secret string of numbers that shows which letters it uses.
When looking for two words that do not share any letters, the secret strings are compared. If the result of comparing them is zero, it means they have no buckets in common. This ensures that the words are totally different, like a cat and a dog. Once two words are found that do not overlap, their lengths are multiplied together to see how big the area would be if they were sides of a rectangle. The goal is to keep track of the biggest rectangle found during this search.
Code Quality
Complexity Analysis
As a rule of thumb, the maximum input size for one second execution time is
- factorial: up to 10 elements
- exponential: up to 20 elements
- polynomial: a few thousand elements
- loglinear: up to 100,000 elements
- linear: a few million elements
- logarithmic: a billion elements
- constant: practically unlimited
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++ | Java | Python | TS | ||||||
| WalkCCC | C++ | Java | Python | ||||||||
| leetcode.ca | C++ | Java | Python | Go | TS | ||||||
| LeetcodeHelp | C++ | Java | Python | ||||||||
| MemoryLimitExceeded | C++ | Python | |||||||||
| Progiez | C++ | Java | Python | ||||||||
| github | black-shadows | C++ | |||||||||
| charles-wangkai | Java | ||||||||||
| chenyanzhe | C++ | ||||||||||
| everthis | JS | ||||||||||
| fartem | Ruby | ||||||||||
| FelixFan1122 | C# | ||||||||||
| fengvyi | C++ | ||||||||||
| fpdjsns | C++ | ||||||||||
| halfrost | Go | ||||||||||
| haoel | C++ | ||||||||||
| JoshCrozier | JS | ||||||||||
| keineahnung2345 | C++ | ||||||||||
| linouk23 | C++ | ||||||||||
| lkwq007 | Python | ||||||||||
| ruoru | Rust | ||||||||||
| suxrobGM | C# | ||||||||||
| varunu28 | Java | ||||||||||
| vli02 | C | ||||||||||
| vthwang | Java | ||||||||||
| wisdompeak | C++ | ||||||||||
| XiaotaoGuo | C++ | ||||||||||
| YuriSpiridonov | Python | ||||||||||
| zfang399 | C++ | ||||||||||
| AlgoMonster | C++, Python, Java, TypeScript | ||||||||||
| WalkCCC | C++, Python, Java | ||||||||||
| leetcode.ca | C++, Go, Java, Python, TypeScript | ||||||||||
| LeetcodeHelp | C++, Java, Python | ||||||||||
| MemoryLimitExceeded | Python, C++ | ||||||||||
| Progiez | C++, Java, Python | ||||||||||
| black-shadows | C++, C++ | ||||||||||
| charles-wangkai | Java | ||||||||||
| chenyanzhe | C++ | ||||||||||
| everthis | JavaScript | ||||||||||
| fartem | Ruby | ||||||||||
| FelixFan1122 | C# | ||||||||||
| fengvyi | C++ | ||||||||||
| fpdjsns | C++ | ||||||||||
| halfrost | Go | ||||||||||
| haoel | C++ | ||||||||||
| JoshCrozier | JavaScript | ||||||||||
| keineahnung2345 | C++ | ||||||||||
| linouk23 | C++ | ||||||||||
| lkwq007 | Python | ||||||||||
| ruoru | Rust | ||||||||||
| suxrobGM | C# | ||||||||||
| varunu28 | Java | ||||||||||
| vli02 | C | ||||||||||
| vthwang | Java | ||||||||||
| wisdompeak | C++ | ||||||||||
| XiaotaoGuo | C++ | ||||||||||
| YuriSpiridonov | Python | ||||||||||
| zfang399 | C++ |
Videos
Solved On The Same Day
- problem 415: Add Strings
- problem 463: Island Perimeter
- problem 541: Reverse String II
- problem 1211: Queries Quality and Percentage
- problem 1251: Average Selling Price
- problem 1280: Students and Examinations
- problem 1327: List the Products Ordered in a Period
- problem 1378: Replace Employee ID With The Unique Identifier
- problem 1407: Top Travellers
- problem 1484: Group Sold Products By The Date
- problem 1517: Find Users With Valid E-Mails
- problem 1527: Patients With a Condition
- problem 1581: Customer Who Visited but Did Not Make Any Transactions
- problem 1587: Bank Account Summary II
- problem 1633: Percentage of Users Attended a Contest
- problem 1661: Average Time of Process per Machine
- problem 1667: Fix Names in a Table
- problem 1683: Invalid Tweets
- problem 1693: Daily Leads and Partners
- problem 1729: Find Followers Count
- problem 1731: The Number of Employees Which Report to Each Employee
- problem 1741: Find Total Time Spent by Each Employee
- problem 1757: Recyclable and Low Fat Products
- problem 1789: Primary Department for Each Employee
- problem 1795: Rearrange Products Table
- problem 1873: Calculate Special Bonus
- problem 1890: The Latest Login in 2020
- problem 1965: Employees With Missing Information
- problem 1978: Employees Whose Manager Left the Company
- problem 2356: Number of Unique Subjects Taught by Each Teacher
- problem 166: Fraction to Recurring Decimal
- problem 223: Rectangle Area
- problem 240: Search a 2D Matrix II
- problem 357: Count Numbers with Unique Digits
- problem 394: Decode String
- problem 1158: Market Analysis I
- problem 1341: Movie Rating
- problem 1492: The kth Factor of n
- problem 1498: Number of Subsequences That Satisfy the Given Sum Condition
- problem 1907: Count Salary Categories
- problem 1934: Confirmation Rate
- problem 2405: Optimal Partition of String
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