Bird
Raised Fist0
Interview Prepsubsets-combinationshardAmazonGoogleFacebook

Number of Valid Words for Each Puzzle

Choose your preparation mode4 modes available
🎯
Number of Valid Words for Each Puzzle
hardBACKTRACKINGAmazonGoogleFacebook

Imagine you have a list of secret words and a set of puzzles, each puzzle containing a set of letters. You want to find out how many words can be formed from each puzzle under certain letter constraints.

💡 This problem is about efficiently counting how many words fit specific letter constraints from puzzles. Beginners often struggle because the naive approach involves checking every word against every puzzle, which is too slow. Understanding bitmasking and subset enumeration is key to solving it efficiently.
📋
Problem Statement

You are given a list of words and a list of puzzles. For each puzzle, you need to find the number of words that satisfy the following conditions: (1) The word contains the first letter of the puzzle. (2) Every letter in the word is contained in the puzzle. Return an array of integers where each element is the number of valid words for the corresponding puzzle.

1 ≤ words.length ≤ 10^54 ≤ words[i].length ≤ 501 ≤ puzzles.length ≤ 10^4puzzles[i].length == 7All strings contain only lowercase English lettersEach puzzle contains unique letters
💡
Example
Input"words = [\"aaaa\",\"asas\",\"able\",\"ability\",\"actt\",\"actor\",\"access\"], puzzles = [\"aboveyz\",\"abrodyz\",\"abslute\",\"absoryz\",\"actresz\",\"gaswxyz\"]"
Output[1,1,3,2,4,0]

For puzzle "aboveyz", only "aaaa" is valid because it contains 'a' and all letters are in the puzzle. For "abslute", words "able", "ability", and "actt" are valid, etc.

  • Words with letters not in any puzzle → 0 count
  • Puzzles with letters not in any word → 0 count
  • Words containing repeated letters → count only once if valid
  • Puzzles where first letter is rare → fewer matches
⚠️
Common Mistakes
Not filtering words with more than 7 unique letters

Unnecessary large frequency map and slower subset enumeration

✅ Skip words with unique letter count > 7

Forgetting to include the puzzle's first letter in subset enumeration

Incorrect counts, missing valid words

✅ Always include the first letter bit in subsets when checking frequency

Using string sets instead of bitmasks for subset checks

Slower performance due to repeated set operations

✅ Use integer bitmasks for O(1) subset checks

Enumerating all subsets of puzzle letters including the first letter

Double counting or missing the mandatory first letter condition

✅ Enumerate subsets only of letters excluding the first letter, then add first letter bit

🧠
Brute Force (Check Each Word Against Each Puzzle)
💡 This approach exists to establish a baseline understanding. It is straightforward but inefficient, helping beginners grasp the problem's requirements before optimizing.

Intuition

For each puzzle, check every word to see if it contains the puzzle's first letter and if all letters of the word are in the puzzle.

Algorithm

  1. For each puzzle, extract its first letter and the set of letters in the puzzle.
  2. For each word, check if it contains the puzzle's first letter.
  3. Check if all letters of the word are contained in the puzzle's letters.
  4. Count the number of words that satisfy both conditions for each puzzle.
💡 The nested loops and letter checks are easy to understand but lead to a large number of comparisons, making it slow for big inputs.
</>
Code
from typing import List

def findNumOfValidWords(words: List[str], puzzles: List[str]) -> List[int]:
    res = []
    for puzzle in puzzles:
        count = 0
        first = puzzle[0]
        puzzle_set = set(puzzle)
        for word in words:
            if first in word and set(word).issubset(puzzle_set):
                count += 1
        res.append(count)
    return res

# Driver code
if __name__ == "__main__":
    words = ["aaaa","asas","able","ability","actt","actor","access"]
    puzzles = ["aboveyz","abrodyz","abslute","absoryz","actresz","gaswxyz"]
    print(findNumOfValidWords(words, puzzles))
Line Notes
for puzzle in puzzles:Iterate over each puzzle to compute its valid word count
first = puzzle[0]Store the first letter of the puzzle, a mandatory letter for valid words
puzzle_set = set(puzzle)Convert puzzle letters to a set for quick membership checks
if first in word and set(word).issubset(puzzle_set):Check both conditions: first letter presence and all letters in puzzle
import java.util.*;

public class Solution {
    public static List<Integer> findNumOfValidWords(String[] words, String[] puzzles) {
        List<Integer> res = new ArrayList<>();
        for (String puzzle : puzzles) {
            int count = 0;
            char first = puzzle.charAt(0);
            Set<Character> puzzleSet = new HashSet<>();
            for (char c : puzzle.toCharArray()) puzzleSet.add(c);
            for (String word : words) {
                if (word.indexOf(first) != -1 && word.chars().allMatch(c -> puzzleSet.contains((char) c))) {
                    count++;
                }
            }
            res.add(count);
        }
        return res;
    }

    public static void main(String[] args) {
        String[] words = {"aaaa","asas","able","ability","actt","actor","access"};
        String[] puzzles = {"aboveyz","abrodyz","abslute","absoryz","actresz","gaswxyz"};
        System.out.println(findNumOfValidWords(words, puzzles));
    }
}
Line Notes
for (String puzzle : puzzles)Loop over each puzzle to process
char first = puzzle.charAt(0);Extract the first letter of the puzzle
Set<Character> puzzleSet = new HashSet<>();Store puzzle letters for quick lookup
if (word.indexOf(first) != -1 && word.chars().allMatch(...))Check if word contains first letter and all letters are in puzzle
#include <bits/stdc++.h>
using namespace std;

vector<int> findNumOfValidWords(vector<string>& words, vector<string>& puzzles) {
    vector<int> res;
    for (auto& puzzle : puzzles) {
        int count = 0;
        char first = puzzle[0];
        unordered_set<char> puzzleSet(puzzle.begin(), puzzle.end());
        for (auto& word : words) {
            if (word.find(first) != string::npos) {
                bool valid = true;
                for (char c : word) {
                    if (puzzleSet.find(c) == puzzleSet.end()) {
                        valid = false;
                        break;
                    }
                }
                if (valid) count++;
            }
        }
        res.push_back(count);
    }
    return res;
}

int main() {
    vector<string> words = {"aaaa","asas","able","ability","actt","actor","access"};
    vector<string> puzzles = {"aboveyz","abrodyz","abslute","absoryz","actresz","gaswxyz"};
    vector<int> res = findNumOfValidWords(words, puzzles);
    for (int x : res) cout << x << " ";
    cout << endl;
    return 0;
}
Line Notes
for (auto& puzzle : puzzles)Iterate over each puzzle
char first = puzzle[0];Store the first letter of the puzzle
unordered_set<char> puzzleSet(puzzle.begin(), puzzle.end());Create a set of puzzle letters for O(1) lookup
if (word.find(first) != string::npos)Check if word contains the puzzle's first letter
function findNumOfValidWords(words, puzzles) {
    const res = [];
    for (const puzzle of puzzles) {
        let count = 0;
        const first = puzzle[0];
        const puzzleSet = new Set(puzzle);
        for (const word of words) {
            if (word.includes(first) && [...word].every(c => puzzleSet.has(c))) {
                count++;
            }
        }
        res.push(count);
    }
    return res;
}

// Driver code
const words = ["aaaa","asas","able","ability","actt","actor","access"];
const puzzles = ["aboveyz","abrodyz","abslute","absoryz","actresz","gaswxyz"];
console.log(findNumOfValidWords(words, puzzles));
Line Notes
for (const puzzle of puzzles)Loop over each puzzle
const first = puzzle[0];Extract the first letter of the puzzle
const puzzleSet = new Set(puzzle);Create a set for quick letter membership checks
if (word.includes(first) && [...word].every(c => puzzleSet.has(c)))Check if word contains first letter and all letters are in puzzle
⏱
Complexity
TimeO(W * P * L) where W = number of words, P = number of puzzles, L = average word length
SpaceO(W * L + P * L) for storing words and puzzles

For each puzzle, we check every word and verify letter conditions, leading to a large nested iteration.

💡 For 10^5 words and 10^4 puzzles, this means 10^9 checks, which is impractical in interviews.
Interview Verdict: TLE

This approach is too slow for large inputs but is useful to understand the problem and constraints before optimizing.

🧠
Bitmask + Hash Map Frequency Counting
💡 This approach introduces bitmasking to represent words and puzzles as integers, enabling efficient subset checks and frequency counting.

Intuition

Represent each word as a bitmask of letters. Count frequencies of these masks. For each puzzle, enumerate all subsets of its letters (excluding the first letter) and sum frequencies of valid subsets that include the first letter.

Algorithm

  1. Convert each word into a bitmask representing its unique letters and count frequencies in a hashmap.
  2. For each puzzle, convert its letters into a bitmask and identify the bitmask of the first letter.
  3. Enumerate all subsets of the puzzle's letters excluding the first letter using bitmask subset enumeration.
  4. For each subset, add the frequency of the subset combined with the first letter bitmask to the answer.
💡 The key insight is that enumerating subsets of puzzle letters is efficient because puzzles have fixed length 7, so subsets are at most 2^6=64.
</>
Code
from typing import List

def findNumOfValidWords(words: List[str], puzzles: List[str]) -> List[int]:
    freq = {}
    for word in words:
        mask = 0
        for ch in set(word):
            mask |= 1 << (ord(ch) - ord('a'))
        if bin(mask).count('1') <= 7:
            freq[mask] = freq.get(mask, 0) + 1

    res = []
    for puzzle in puzzles:
        first = 1 << (ord(puzzle[0]) - ord('a'))
        mask = 0
        for ch in puzzle[1:]:
            mask |= 1 << (ord(ch) - ord('a'))

        submask = mask
        total = 0
        while True:
            s = submask | first
            if s in freq:
                total += freq[s]
            if submask == 0:
                break
            submask = (submask - 1) & mask
        res.append(total)
    return res

# Driver code
if __name__ == "__main__":
    words = ["aaaa","asas","able","ability","actt","actor","access"]
    puzzles = ["aboveyz","abrodyz","abslute","absoryz","actresz","gaswxyz"]
    print(findNumOfValidWords(words, puzzles))
Line Notes
for word in words:Iterate over each word to build frequency map
mask |= 1 << (ord(ch) - ord('a'))Set bit corresponding to each unique letter in the word
if bin(mask).count('1') <= 7:Ignore words with more than 7 unique letters since puzzles have 7 letters
while True:Enumerate all subsets of puzzle letters excluding first letter using bitmask subset enumeration
import java.util.*;

public class Solution {
    public static List<Integer> findNumOfValidWords(String[] words, String[] puzzles) {
        Map<Integer, Integer> freq = new HashMap<>();
        for (String word : words) {
            int mask = 0;
            for (char c : word.toCharArray()) {
                mask |= 1 << (c - 'a');
            }
            if (Integer.bitCount(mask) <= 7) {
                freq.put(mask, freq.getOrDefault(mask, 0) + 1);
            }
        }

        List<Integer> res = new ArrayList<>();
        for (String puzzle : puzzles) {
            int first = 1 << (puzzle.charAt(0) - 'a');
            int mask = 0;
            for (int i = 1; i < 7; i++) {
                mask |= 1 << (puzzle.charAt(i) - 'a');
            }

            int submask = mask;
            int total = 0;
            while (true) {
                int s = submask | first;
                total += freq.getOrDefault(s, 0);
                if (submask == 0) break;
                submask = (submask - 1) & mask;
            }
            res.add(total);
        }
        return res;
    }

    public static void main(String[] args) {
        String[] words = {"aaaa","asas","able","ability","actt","actor","access"};
        String[] puzzles = {"aboveyz","abrodyz","abslute","absoryz","actresz","gaswxyz"};
        System.out.println(findNumOfValidWords(words, puzzles));
    }
}
Line Notes
for (String word : words)Build frequency map of word bitmasks
mask |= 1 << (c - 'a');Set bit for each letter in word
if (Integer.bitCount(mask) <= 7)Filter out words with more than 7 unique letters
while (true)Enumerate all subsets of puzzle letters excluding first letter
#include <bits/stdc++.h>
using namespace std;

vector<int> findNumOfValidWords(vector<string>& words, vector<string>& puzzles) {
    unordered_map<int,int> freq;
    for (auto& word : words) {
        int mask = 0;
        unordered_set<char> unique_chars(word.begin(), word.end());
        for (char c : unique_chars) mask |= 1 << (c - 'a');
        if (__builtin_popcount(mask) <= 7) freq[mask]++;
    }

    vector<int> res;
    for (auto& puzzle : puzzles) {
        int first = 1 << (puzzle[0] - 'a');
        int mask = 0;
        for (int i = 1; i < 7; i++) mask |= 1 << (puzzle[i] - 'a');

        int submask = mask;
        int total = 0;
        while (true) {
            int s = submask | first;
            if (freq.count(s)) total += freq[s];
            if (submask == 0) break;
            submask = (submask - 1) & mask;
        }

        res.push_back(total);
    }
    return res;
}

int main() {
    vector<string> words = {"aaaa","asas","able","ability","actt","actor","access"};
    vector<string> puzzles = {"aboveyz","abrodyz","abslute","absoryz","actresz","gaswxyz"};
    vector<int> res = findNumOfValidWords(words, puzzles);
    for (int x : res) cout << x << " ";
    cout << endl;
    return 0;
}
Line Notes
unordered_map<int,int> freq;Map bitmask to frequency of words
for (char c : unique_chars) mask |= 1 << (c - 'a');Create bitmask for unique letters in word
if (__builtin_popcount(mask) <= 7)Ignore words with more than 7 unique letters
while (true) { ... }Enumerate all subsets of puzzle letters excluding first letter
function findNumOfValidWords(words, puzzles) {
    const freq = new Map();
    for (const word of words) {
        let mask = 0;
        const unique = new Set(word);
        for (const ch of unique) {
            mask |= 1 << (ch.charCodeAt(0) - 97);
        }
        if (countBits(mask) <= 7) {
            freq.set(mask, (freq.get(mask) || 0) + 1);
        }
    }

    function countBits(x) {
        let count = 0;
        while (x) {
            count += x & 1;
            x >>= 1;
        }
        return count;
    }

    const res = [];
    for (const puzzle of puzzles) {
        const first = 1 << (puzzle.charCodeAt(0) - 97);
        let mask = 0;
        for (let i = 1; i < 7; i++) {
            mask |= 1 << (puzzle.charCodeAt(i) - 97);
        }

        let submask = mask;
        let total = 0;
        while (true) {
            const s = submask | first;
            if (freq.has(s)) total += freq.get(s);
            if (submask === 0) break;
            submask = (submask - 1) & mask;
        }
        res.push(total);
    }
    return res;
}

// Driver code
const words = ["aaaa","asas","able","ability","actt","actor","access"];
const puzzles = ["aboveyz","abrodyz","abslute","absoryz","actresz","gaswxyz"];
console.log(findNumOfValidWords(words, puzzles));
Line Notes
for (const word of words)Build frequency map of word bitmasks
mask |= 1 << (ch.charCodeAt(0) - 97);Set bit for each unique letter in word
if (countBits(mask) <= 7)Filter words with more than 7 unique letters
while (true)Enumerate all subsets of puzzle letters excluding first letter
⏱
Complexity
TimeO(W * L + P * 2^6) where W = number of words, P = number of puzzles, L = average word length
SpaceO(W + P) for frequency map and results

Bitmasking compresses word letters and subset enumeration over 6 letters is efficient, drastically reducing checks.

💡 For 10^5 words and 10^4 puzzles, this approach is feasible because 2^6=64 subsets per puzzle is manageable.
Interview Verdict: Accepted

This approach is efficient and practical for interview coding, demonstrating mastery of bitmasking and subset enumeration.

🧠
Bitmask + Trie Optimization (Advanced)
💡 This approach uses a trie data structure to store word bitmasks and prune search space when enumerating puzzle subsets, further optimizing performance.

Intuition

Build a trie where each node represents a letter bit. For each puzzle, traverse the trie only along paths that correspond to puzzle letters and include the first letter, counting valid words efficiently.

Algorithm

  1. Build a trie where each path corresponds to a unique combination of letters from words.
  2. Each trie node stores the count of words ending at that node.
  3. For each puzzle, recursively traverse the trie, only following branches corresponding to puzzle letters.
  4. Ensure the first letter of the puzzle is included in the path before counting words.
💡 Trie traversal prunes invalid subsets early, avoiding enumeration of all subsets explicitly.
</>
Code
from typing import List

class TrieNode:
    def __init__(self):
        self.count = 0
        self.children = {}

class Solution:
    def __init__(self):
        self.root = TrieNode()

    def insert(self, mask):
        node = self.root
        for i in range(26):
            bit = (mask >> i) & 1
            if bit:
                if i not in node.children:
                    node.children[i] = TrieNode()
                node = node.children[i]
        node.count += 1

    def dfs(self, node, puzzle_mask, first_bit, has_first):
        res = node.count if has_first else 0
        for i, child in node.children.items():
            if (puzzle_mask & (1 << i)) != 0:
                res += self.dfs(child, puzzle_mask, first_bit, has_first or (i == first_bit))
        return res

    def findNumOfValidWords(self, words: List[str], puzzles: List[str]) -> List[int]:
        for word in words:
            mask = 0
            for ch in set(word):
                mask |= 1 << (ord(ch) - ord('a'))
            if bin(mask).count('1') <= 7:
                self.insert(mask)

        res = []
        for puzzle in puzzles:
            first_bit = ord(puzzle[0]) - ord('a')
            puzzle_mask = 0
            for ch in puzzle:
                puzzle_mask |= 1 << (ord(ch) - ord('a'))
            res.append(self.dfs(self.root, puzzle_mask, first_bit, False))
        return res

# Driver code
if __name__ == "__main__":
    words = ["aaaa","asas","able","ability","actt","actor","access"]
    puzzles = ["aboveyz","abrodyz","abslute","absoryz","actresz","gaswxyz"]
    sol = Solution()
    print(sol.findNumOfValidWords(words, puzzles))
Line Notes
class TrieNode:Define trie node with count and children
def insert(self, mask):Insert word bitmask into trie by bits set
def dfs(self, node, puzzle_mask, first_bit, has_first):Recursively traverse trie pruning by puzzle letters
res = node.count if has_first else 0Count words only if first letter included in path
import java.util.*;

class TrieNode {
    int count = 0;
    Map<Integer, TrieNode> children = new HashMap<>();
}

public class Solution {
    TrieNode root = new TrieNode();

    void insert(int mask) {
        TrieNode node = root;
        for (int i = 0; i < 26; i++) {
            if (((mask >> i) & 1) == 1) {
                node.children.putIfAbsent(i, new TrieNode());
                node = node.children.get(i);
            }
        }
        node.count++;
    }

    int dfs(TrieNode node, int puzzleMask, int firstBit, boolean hasFirst) {
        int res = hasFirst ? node.count : 0;
        for (Map.Entry<Integer, TrieNode> entry : node.children.entrySet()) {
            int i = entry.getKey();
            TrieNode child = entry.getValue();
            if ((puzzleMask & (1 << i)) != 0) {
                res += dfs(child, puzzleMask, firstBit, hasFirst || i == firstBit);
            }
        }
        return res;
    }

    public List<Integer> findNumOfValidWords(String[] words, String[] puzzles) {
        for (String word : words) {
            int mask = 0;
            for (char c : word.toCharArray()) {
                mask |= 1 << (c - 'a');
            }
            if (Integer.bitCount(mask) <= 7) insert(mask);
        }

        List<Integer> res = new ArrayList<>();
        for (String puzzle : puzzles) {
            int firstBit = puzzle.charAt(0) - 'a';
            int puzzleMask = 0;
            for (char c : puzzle.toCharArray()) puzzleMask |= 1 << (c - 'a');
            res.add(dfs(root, puzzleMask, firstBit, false));
        }
        return res;
    }

    public static void main(String[] args) {
        String[] words = {"aaaa","asas","able","ability","actt","actor","access"};
        String[] puzzles = {"aboveyz","abrodyz","abslute","absoryz","actresz","gaswxyz"};
        Solution sol = new Solution();
        System.out.println(sol.findNumOfValidWords(words, puzzles));
    }
}
Line Notes
class TrieNode {Trie node with count and children map
void insert(int mask) {Insert word bitmask into trie by bits set
int dfs(TrieNode node, int puzzleMask, int firstBit, boolean hasFirst) {DFS trie pruning by puzzle letters
int res = hasFirst ? node.count : 0;Count words only if first letter included
#include <bits/stdc++.h>
using namespace std;

struct TrieNode {
    int count = 0;
    unordered_map<int, TrieNode*> children;
};

class Solution {
    TrieNode* root = new TrieNode();

    void insert(int mask) {
        TrieNode* node = root;
        for (int i = 0; i < 26; i++) {
            if ((mask >> i) & 1) {
                if (!node->children.count(i)) node->children[i] = new TrieNode();
                node = node->children[i];
            }
        }
        node->count++;
    }

    int dfs(TrieNode* node, int puzzleMask, int firstBit, bool hasFirst) {
        int res = hasFirst ? node->count : 0;
        for (auto& [i, child] : node->children) {
            if ((puzzleMask & (1 << i)) != 0) {
                res += dfs(child, puzzleMask, firstBit, hasFirst || i == firstBit);
            }
        }
        return res;
    }

public:
    vector<int> findNumOfValidWords(vector<string>& words, vector<string>& puzzles) {
        for (auto& word : words) {
            int mask = 0;
            unordered_set<char> unique_chars(word.begin(), word.end());
            for (char c : unique_chars) mask |= 1 << (c - 'a');
            if (__builtin_popcount(mask) <= 7) insert(mask);
        }

        vector<int> res;
        for (auto& puzzle : puzzles) {
            int firstBit = puzzle[0] - 'a';
            int puzzleMask = 0;
            for (char c : puzzle) puzzleMask |= 1 << (c - 'a');
            res.push_back(dfs(root, puzzleMask, firstBit, false));
        }
        return res;
    }
};

int main() {
    vector<string> words = {"aaaa","asas","able","ability","actt","actor","access"};
    vector<string> puzzles = {"aboveyz","abrodyz","abslute","absoryz","actresz","gaswxyz"};
    Solution sol;
    vector<int> res = sol.findNumOfValidWords(words, puzzles);
    for (int x : res) cout << x << " ";
    cout << endl;
    return 0;
}
Line Notes
struct TrieNode {Trie node with count and children map
void insert(int mask) {Insert word bitmask into trie by bits set
int dfs(TrieNode* node, int puzzleMask, int firstBit, bool hasFirst) {DFS trie pruning by puzzle letters
int res = hasFirst ? node->count : 0;Count words only if first letter included
class TrieNode {
    constructor() {
        this.count = 0;
        this.children = new Map();
    }
}

class Solution {
    constructor() {
        this.root = new TrieNode();
    }

    insert(mask) {
        let node = this.root;
        for (let i = 0; i < 26; i++) {
            if ((mask >> i) & 1) {
                if (!node.children.has(i)) node.children.set(i, new TrieNode());
                node = node.children.get(i);
            }
        }
        node.count++;
    }

    dfs(node, puzzleMask, firstBit, hasFirst) {
        let res = hasFirst ? node.count : 0;
        for (let [i, child] of node.children.entries()) {
            if ((puzzleMask & (1 << i)) !== 0) {
                res += this.dfs(child, puzzleMask, firstBit, hasFirst || i === firstBit);
            }
        }
        return res;
    }

    findNumOfValidWords(words, puzzles) {
        for (const word of words) {
            let mask = 0;
            const unique = new Set(word);
            for (const ch of unique) {
                mask |= 1 << (ch.charCodeAt(0) - 97);
            }
            if (this.countBits(mask) <= 7) this.insert(mask);
        }

        const res = [];
        for (const puzzle of puzzles) {
            const firstBit = puzzle.charCodeAt(0) - 97;
            let puzzleMask = 0;
            for (const ch of puzzle) {
                puzzleMask |= 1 << (ch.charCodeAt(0) - 97);
            }
            res.push(this.dfs(this.root, puzzleMask, firstBit, false));
        }
        return res;
    }

    countBits(x) {
        let count = 0;
        while (x) {
            count += x & 1;
            x >>= 1;
        }
        return count;
    }
}

// Driver code
const words = ["aaaa","asas","able","ability","actt","actor","access"];
const puzzles = ["aboveyz","abrodyz","abslute","absoryz","actresz","gaswxyz"];
const sol = new Solution();
console.log(sol.findNumOfValidWords(words, puzzles));
Line Notes
class TrieNode {Trie node with count and children map
insert(mask) {Insert word bitmask into trie by bits set
dfs(node, puzzleMask, firstBit, hasFirst) {DFS trie pruning by puzzle letters
let res = hasFirst ? node.count : 0;Count words only if first letter included
⏱
Complexity
TimeO(W * L + P * M) where W = number of words, L = average word length, P = number of puzzles, M = trie traversal nodes per puzzle
SpaceO(W * L) for trie storage

Trie pruning reduces subset enumeration overhead by exploring only valid letter paths per puzzle.

💡 This approach is more complex but can be faster in practice when many words share letter subsets.
Interview Verdict: Accepted (Advanced)

This approach is suitable for candidates comfortable with tries and bitmasking, showing advanced optimization skills.

📊
All Approaches - One-Glance Tradeoffs
💡 Use the bitmask + hashmap approach in 95% of interviews for balance of clarity and efficiency.
ApproachTimeSpaceStack RiskReconstructUse In Interview
1. Brute ForceO(W * P * L)O(W * L + P * L)NoN/AMention only - never code due to inefficiency
2. Bitmask + Hash Map Frequency CountingO(W * L + P * 2^6)O(W + P)NoN/ACode this approach for optimal balance
3. Bitmask + Trie OptimizationO(W * L + P * M)O(W * L)Yes (deep recursion)N/AMention as advanced optimization if asked
💼
Interview Strategy
💡 Use this guide to understand the problem deeply, starting from brute force and progressing to optimal bitmask solutions. Practice explaining each step clearly.

How to Present

Clarify problem constraints and requirements.Explain brute force approach and its inefficiency.Introduce bitmasking and frequency map optimization.Discuss subset enumeration for puzzles.Optionally mention trie optimization if time permits.

Time Allocation

Clarify: 3min → Approach: 5min → Code: 10min → Test: 5min. Total ~23min

What the Interviewer Tests

Understanding of bitmasking, subset enumeration, pruning techniques, and ability to optimize brute force solutions.

Common Follow-ups

  • What if puzzles have more than 7 letters? → Subset enumeration becomes expensive, consider pruning or different data structures.
  • Can you optimize space usage? → Use integer bitmasks and avoid storing full strings.
💡 Follow-ups test your ability to adapt the solution to variations and optimize resource usage.
🔍
Pattern Recognition

When to Use

1) You need to check subsets of letters efficiently. 2) There is a mandatory letter constraint. 3) Input size is large, requiring optimization. 4) Bitmasking can represent letter sets.

Signature Phrases

'first letter of the puzzle''all letters of the word are in the puzzle'

NOT This Pattern When

Problems that require substring or subsequence matching without letter set constraints

Similar Problems

Word Squares - also uses backtracking and letter constraintsMaximum XOR of Two Numbers in an Array - bitmasking for optimizationNumber of Matching Subsequences - subset and frequency counting

Practice

(1/5)
1. Consider the following code snippet that generates all combinations of size k from numbers 1 to n using an iterative lexicographic approach. What is the final output of combine(4, 2)?
easy
A. [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
B. [[1, 2], [2, 3], [3, 4]]
C. [[1, 2], [1, 3], [2, 3], [2, 4], [3, 4]]
D. [[1, 2], [1, 3], [1, 4], [2, 3], [3, 4]]

Solution

  1. Step 1: Trace initial combination and increments

    Start with [1, 2], then increment last element until max, then increment previous and reset subsequent.
  2. Step 2: Enumerate all combinations of size 2 from 1 to 4 in lex order

    All pairs: [1,2], [1,3], [1,4], [2,3], [2,4], [3,4] are generated.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Output matches all 6 combinations of 4 choose 2 [OK]
Hint: Lexicographic iteration generates all combinations in sorted order [OK]
Common Mistakes:
  • Missing some combinations due to off-by-one in increments
2. What is the time complexity of the iterative lexicographic combinations generation algorithm for generating all combinations of size k from n elements?
medium
A. O(n^k) because each element can be chosen or not
B. O(k * (n choose k)) since each combination of size k is generated once and updated in O(k)
C. O(\u03C3(n choose k) * k) where \u03C3 is the number of combinations generated
D. O(n * k) because the outer loop runs n times and inner loop k times

Solution

  1. Step 1: Identify number of combinations

    There are exactly (n choose k) combinations to generate.
  2. Step 2: Analyze per-combination cost

    Each combination is generated and updated in O(k) time due to copying and resetting elements.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Time is proportional to number of combinations times combination size [OK]
Hint: Time depends on number of combinations times k [OK]
Common Mistakes:
  • Confusing with exponential O(n^k) or linear O(n*k) complexities
3. Examine the following buggy code snippet for letter case permutation. Which line contains the subtle bug that causes incorrect results on inputs with digits?
def letterCasePermutation(s: str) -> List[str]:
    res = []
    arr = list(s)
    def backtrack(i: int):
        if i == len(arr):
            res.append(''.join(arr))
            return
        if arr[i].isdigit():
            arr[i] = arr[i].upper()  # BUG: digits have no case
            backtrack(i + 1)
        else:
            arr[i] = arr[i].lower()
            backtrack(i + 1)
            arr[i] = arr[i].upper()
            backtrack(i + 1)
            arr[i] = s[i]
    backtrack(0)
    return res
medium
A. Line with 'if i == len(arr):'
B. Line with 'arr[i] = arr[i].upper() # BUG: digits have no case'
C. Line with 'if arr[i].isdigit():'
D. Line with 'arr[i] = s[i]' to revert changes

Solution

  1. Step 1: Analyze digit handling

    Digits have no uppercase or lowercase forms, so calling upper() on a digit is unnecessary and incorrect.
  2. Step 2: Identify bug impact

    Modifying digits causes incorrect permutations or corrupted results; digits should be left unchanged and recursion called directly.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Digits must be skipped, not toggled [OK]
Hint: Digits must not be modified or toggled [OK]
Common Mistakes:
  • Forgetting to revert changes
  • Modifying digits as letters
4. Examine the following buggy code snippet for partitioning an array into k equal sum subsets using backtracking with bitmask memoization. Which line contains the subtle bug that can cause incorrect results or inefficiency?
medium
A. Line 2: missing check if total is divisible by k
B. Line 6: sorting nums in descending order
C. Line 14: memoizing result after recursive call
D. Line 18: checking if element is unused and fits in current subset

Solution

  1. Step 1: Identify missing early pruning

    Line 2 calculates total but does not check if total % k != 0, which is critical to avoid futile search.
  2. Step 2: Verify other lines

    Sorting descending (line 6) is correct for pruning; memoization (line 14) and usage check (line 18) are correct.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Without divisibility check, algorithm wastes time exploring impossible partitions [OK]
Hint: Always check divisibility before backtracking [OK]
Common Mistakes:
  • Forgetting early divisibility check
  • Misplacing memoization
5. The following backtracking code attempts to generate all subsets of nums. What is the subtle bug causing incorrect output?
medium
A. Using global variable without resetting between calls
B. Not copying the current subset before appending to result causes all subsets to be the same list
C. Base case missing the empty subset
D. Forgetting to pop after including an element causes subsets to accumulate extra elements

Solution

  1. Step 1: Analyze base case append

    The code appends path directly without copying, so all appended references point to the same mutable list.
  2. Step 2: Consequence of mutation

    As recursion unwinds and path changes, all entries in result reflect the final state of path, causing incorrect subsets.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Appending a copy (path[:]) fixes the bug [OK]
Hint: Always append a copy of the current path to result [OK]
Common Mistakes:
  • Forgetting to pop after append
  • Misunderstanding base case inclusion
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard