🧠
Bitmasking with Length Sorting and Early Pruning
💡 Sorting words by length allows early pruning of pairs that cannot beat the current max product, improving efficiency.
Intuition
Sort words by length descending. When checking pairs, if product of lengths is less than current max, skip further checks.
Algorithm
- Convert each word to a bitmask and store its length.
- Sort words by length descending.
- Iterate over pairs; if product of lengths is less than current max, break inner loop early.
- If bitmasks have no common letters, update max product.
- Return the maximum product found.
💡 Sorting and pruning reduce unnecessary checks, speeding up the solution especially when many words are short.
def maxProduct(words):
n = len(words)
masks = []
for word in words:
mask = 0
for ch in word:
mask |= 1 << (ord(ch) - ord('a'))
masks.append((mask, len(word)))
masks.sort(key=lambda x: x[1], reverse=True)
max_prod = 0
for i in range(n):
if masks[i][1] * masks[i][1] <= max_prod:
break
for j in range(i + 1, n):
if masks[i][1] * masks[j][1] <= max_prod:
break
if (masks[i][0] & masks[j][0]) == 0:
max_prod = max(max_prod, masks[i][1] * masks[j][1])
return max_prod
# Driver code
if __name__ == '__main__':
words = ["abcw","baz","foo","bar","xtfn","abcdef"]
print(maxProduct(words)) # Output: 16
Line Notes
masks.append((mask, len(word)))Store tuple of bitmask and word length for sorting
masks.sort(key=lambda x: x[1], reverse=True)Sort words by length descending to prune early
if masks[i][1] * masks[i][1] <= max_prodIf max possible product with current word is less than max, break outer loop
if masks[i][1] * masks[j][1] <= max_prodPrune inner loop if product can't beat max product
if (masks[i][0] & masks[j][0]) == 0Check if words share no letters using bitwise AND
import java.util.*;
public class Solution {
public static int maxProduct(String[] words) {
int n = words.length;
int[][] masks = new int[n][2];
for (int i = 0; i < n; i++) {
int mask = 0;
for (char c : words[i].toCharArray()) {
mask |= 1 << (c - 'a');
}
masks[i][0] = mask;
masks[i][1] = words[i].length();
}
Arrays.sort(masks, (a, b) -> b[1] - a[1]);
int maxProd = 0;
for (int i = 0; i < n; i++) {
if (masks[i][1] * masks[i][1] <= maxProd) break;
for (int j = i + 1; j < n; j++) {
if (masks[i][1] * masks[j][1] <= maxProd) break;
if ((masks[i][0] & masks[j][0]) == 0) {
maxProd = Math.max(maxProd, masks[i][1] * masks[j][1]);
}
}
}
return maxProd;
}
public static void main(String[] args) {
String[] words = {"abcw","baz","foo","bar","xtfn","abcdef"};
System.out.println(maxProduct(words)); // Output: 16
}
}
Line Notes
int[][] masks = new int[n][2];Store bitmask and length pairs
Arrays.sort(masks, (a, b) -> b[1] - a[1]);Sort by length descending for pruning
if (masks[i][1] * masks[i][1] <= maxProd) break;Prune outer loop if max possible product is less than current max
if (masks[i][1] * masks[j][1] <= maxProd) break;Prune inner loop similarly
if ((masks[i][0] & masks[j][0]) == 0)Check no common letters using bitwise AND
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int maxProduct(vector<string>& words) {
int n = words.size();
vector<pair<int,int>> masks(n);
for (int i = 0; i < n; i++) {
int mask = 0;
for (char c : words[i]) {
mask |= 1 << (c - 'a');
}
masks[i] = {mask, (int)words[i].size()};
}
sort(masks.begin(), masks.end(), [](auto &a, auto &b) {
return a.second > b.second;
});
int maxProd = 0;
for (int i = 0; i < n; i++) {
if (masks[i].second * masks[i].second <= maxProd) break;
for (int j = i + 1; j < n; j++) {
if (masks[i].second * masks[j].second <= maxProd) break;
if ((masks[i].first & masks[j].first) == 0) {
maxProd = max(maxProd, masks[i].second * masks[j].second);
}
}
}
return maxProd;
}
int main() {
vector<string> words = {"abcw","baz","foo","bar","xtfn","abcdef"};
cout << maxProduct(words) << endl; // Output: 16
return 0;
}
Line Notes
vector<pair<int,int>> masks(n);Store bitmask and length pairs
sort(masks.begin(), masks.end(), [](auto &a, auto &b)Sort by length descending for pruning
if (masks[i].second * masks[i].second <= maxProd) break;Prune outer loop if max possible product is less than current max
if (masks[i].second * masks[j].second <= maxProd) break;Prune inner loop similarly
if ((masks[i].first & masks[j].first) == 0)Check no common letters using bitwise AND
function maxProduct(words) {
const n = words.length;
const masks = [];
for (let word of words) {
let mask = 0;
for (const ch of word) {
mask |= 1 << (ch.charCodeAt(0) - 97);
}
masks.push([mask, word.length]);
}
masks.sort((a, b) => b[1] - a[1]);
let maxProd = 0;
for (let i = 0; i < n; i++) {
if (masks[i][1] * masks[i][1] <= maxProd) break;
for (let j = i + 1; j < n; j++) {
if (masks[i][1] * masks[j][1] <= maxProd) break;
if ((masks[i][0] & masks[j][0]) === 0) {
maxProd = Math.max(maxProd, masks[i][1] * masks[j][1]);
}
}
}
return maxProd;
}
// Driver code
const words = ["abcw","baz","foo","bar","xtfn","abcdef"];
console.log(maxProduct(words)); // Output: 16
Line Notes
const masks = [];Store bitmask and length pairs
masks.sort((a, b) => b[1] - a[1]);Sort by length descending for pruning
if (masks[i][1] * masks[i][1] <= maxProd) break;Prune outer loop if max possible product is less than current max
if (masks[i][1] * masks[j][1] <= maxProd) break;Prune inner loop similarly
if ((masks[i][0] & masks[j][0]) === 0)Check no common letters using bitwise AND
TimeO(n^2 + n*k + n log n) where n is number of words and k is average word length
SpaceO(n) for bitmask and length storage
Sorting adds O(n log n), bitmask creation O(n*k), and pairwise checks O(n^2) with pruning to reduce actual checks.
💡 This approach is faster than plain bitmasking for large inputs due to pruning, making it practical for interviews.
Interview Verdict: Accepted / Most efficient practical solution
This approach balances clarity and efficiency, making it ideal to implement in interviews.