Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
puzzle string, a word is valid if both the following conditions are satisfied:
word contains the first letter of puzzle.word, that letter is in puzzle.
"abcdefg", then valid words are "faced", "cabbage", and "baggage", while"beefed" (does not include 'a') and "based" (includes 's' which is not in the puzzle).answer, where answer[i] is the number of words in the given word list words that is valid with respect to the puzzle puzzles[i].
Example 1:
Input: words = ["aaaa","asas","able","ability","actt","actor","access"], puzzles = ["aboveyz","abrodyz","abslute","absoryz","actresz","gaswxyz"] Output: [1,1,3,2,4,0] Explanation: 1 valid word for "aboveyz" : "aaaa" 1 valid word for "abrodyz" : "aaaa" 3 valid words for "abslute" : "aaaa", "asas", "able" 2 valid words for "absoryz" : "aaaa", "asas" 4 valid words for "actresz" : "aaaa", "asas", "actt", "access" There are no valid words for "gaswxyz" cause none of the words in the list contains letter 'g'.
Example 2:
Input: words = ["apple","pleas","please"], puzzles = ["aelwxyz","aelpxyz","aelpsxy","saelpxy","xaelpsy"] Output: [0,1,3,2,0]
Constraints:
1 <= words.length <= 1054 <= words[i].length <= 501 <= puzzles.length <= 104puzzles[i].length == 7words[i] and puzzles[i] consist of lowercase English letters.puzzles[i] does not contain repeated characters.Problem summary: With respect to a given puzzle string, a word is valid if both the following conditions are satisfied: word contains the first letter of puzzle. For each letter in word, that letter is in puzzle. For example, if the puzzle is "abcdefg", then valid words are "faced", "cabbage", and "baggage", while invalid words are "beefed" (does not include 'a') and "based" (includes 's' which is not in the puzzle). Return an array answer, where answer[i] is the number of words in the given word list words that is valid with respect to the puzzle puzzles[i].
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Hash Map · Bit Manipulation · Trie
["aaaa","asas","able","ability","actt","actor","access"] ["aboveyz","abrodyz","abslute","absoryz","actresz","gaswxyz"]
["apple","pleas","please"] ["aelwxyz","aelpxyz","aelpsxy","saelpxy","xaelpsy"]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #1178: Number of Valid Words for Each Puzzle
class Solution {
public List<Integer> findNumOfValidWords(String[] words, String[] puzzles) {
Map<Integer, Integer> cnt = new HashMap<>(words.length);
for (var w : words) {
int mask = 0;
for (int i = 0; i < w.length(); ++i) {
mask |= 1 << (w.charAt(i) - 'a');
}
cnt.merge(mask, 1, Integer::sum);
}
List<Integer> ans = new ArrayList<>();
for (var p : puzzles) {
int mask = 0;
for (int i = 0; i < p.length(); ++i) {
mask |= 1 << (p.charAt(i) - 'a');
}
int x = 0;
int i = p.charAt(0) - 'a';
for (int j = mask; j > 0; j = (j - 1) & mask) {
if ((j >> i & 1) == 1) {
x += cnt.getOrDefault(j, 0);
}
}
ans.add(x);
}
return ans;
}
}
// Accepted solution for LeetCode #1178: Number of Valid Words for Each Puzzle
func findNumOfValidWords(words []string, puzzles []string) (ans []int) {
cnt := map[int]int{}
for _, w := range words {
mask := 0
for _, c := range w {
mask |= 1 << (c - 'a')
}
cnt[mask]++
}
for _, p := range puzzles {
mask := 0
for _, c := range p {
mask |= 1 << (c - 'a')
}
x, i := 0, p[0]-'a'
for j := mask; j > 0; j = (j - 1) & mask {
if j>>i&1 > 0 {
x += cnt[j]
}
}
ans = append(ans, x)
}
return
}
# Accepted solution for LeetCode #1178: Number of Valid Words for Each Puzzle
class Solution:
def findNumOfValidWords(self, words: List[str], puzzles: List[str]) -> List[int]:
cnt = Counter()
for w in words:
mask = 0
for c in w:
mask |= 1 << (ord(c) - ord("a"))
cnt[mask] += 1
ans = []
for p in puzzles:
mask = 0
for c in p:
mask |= 1 << (ord(c) - ord("a"))
x, i, j = 0, ord(p[0]) - ord("a"), mask
while j:
if j >> i & 1:
x += cnt[j]
j = (j - 1) & mask
ans.append(x)
return ans
// Accepted solution for LeetCode #1178: Number of Valid Words for Each Puzzle
/**
* [1178] Number of Valid Words for Each Puzzle
*
* With respect to a given puzzle string, a word is valid if both the following conditions are satisfied:
* word contains the first letter of puzzle.
* For each letter in word, that letter is in puzzle.
*
* For example, if the puzzle is "abcdefg", then valid words are "faced", "cabbage", and "baggage", while
* invalid words are "beefed" (does not include 'a') and "based" (includes 's' which is not in the puzzle).
*
*
* Return an array answer, where answer[i] is the number of words in the given word list words that is valid with respect to the puzzle puzzles[i].
*
* Example 1:
*
* Input: words = ["aaaa","asas","able","ability","actt","actor","access"], puzzles = ["aboveyz","abrodyz","abslute","absoryz","actresz","gaswxyz"]
* Output: [1,1,3,2,4,0]
* Explanation:
* 1 valid word for "aboveyz" : "aaaa"
* 1 valid word for "abrodyz" : "aaaa"
* 3 valid words for "abslute" : "aaaa", "asas", "able"
* 2 valid words for "absoryz" : "aaaa", "asas"
* 4 valid words for "actresz" : "aaaa", "asas", "actt", "access"
* There are no valid words for "gaswxyz" cause none of the words in the list contains letter 'g'.
*
* Example 2:
*
* Input: words = ["apple","pleas","please"], puzzles = ["aelwxyz","aelpxyz","aelpsxy","saelpxy","xaelpsy"]
* Output: [0,1,3,2,0]
*
*
* Constraints:
*
* 1 <= words.length <= 10^5
* 4 <= words[i].length <= 50
* 1 <= puzzles.length <= 10^4
* puzzles[i].length == 7
* words[i] and puzzles[i] consist of lowercase English letters.
* Each puzzles[i] does not contain repeated characters.
*
*/
pub struct Solution {}
// problem: https://leetcode.com/problems/number-of-valid-words-for-each-puzzle/
// discuss: https://leetcode.com/problems/number-of-valid-words-for-each-puzzle/discuss/?currentPage=1&orderBy=most_votes&query=
// submission codes start here
impl Solution {
// Credit: https://leetcode.com/problems/number-of-valid-words-for-each-puzzle/solutions/1529729/rust-using-simple-bitwise-and/
pub fn find_num_of_valid_words(words: Vec<String>, puzzles: Vec<String>) -> Vec<i32> {
let mut words = words
.iter()
.map(|w| Self::word_to_int(w))
.collect::<Vec<_>>();
let mut puzzles = puzzles
.iter()
.map(|w| {
let as_int = Self::word_to_int(w);
let first_ch = Self::first_char_to_mask(w);
(as_int, first_ch)
})
.collect::<Vec<_>>();
let mut result = Vec::with_capacity(puzzles.len());
for &(puzzle, first_ch) in puzzles.iter() {
let mut counter = 0;
for &word in words.iter() {
if (word & puzzle == word) & (word & first_ch == first_ch) {
counter += 1;
}
}
result.push(counter);
}
result
}
fn word_to_int<W: AsRef<str>>(word: W) -> u32 {
let word = word.as_ref().as_bytes();
let mut bits = 0;
for &ch in word {
bits |= 1 << ch - b'a';
}
bits
}
fn first_char_to_mask<W: AsRef<str>>(word: W) -> u32 {
let word = word.as_ref().as_bytes();
1 << word[0] - b'a'
}
}
// submission codes end
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_1178_example_1() {
let words = vec_string!["aaaa", "asas", "able", "ability", "actt", "actor", "access"];
let puzzles = vec_string![
"aboveyz", "abrodyz", "abslute", "absoryz", "actresz", "gaswxyz"
];
let result = vec![1, 1, 3, 2, 4, 0];
assert_eq!(Solution::find_num_of_valid_words(words, puzzles), result);
}
#[test]
fn test_1178_example_2() {
let words = vec_string!["apple", "pleas", "please"];
let puzzles = vec_string!["aelwxyz", "aelpxyz", "aelpsxy", "saelpxy", "xaelpsy"];
let result = vec![0, 1, 3, 2, 0];
assert_eq!(Solution::find_num_of_valid_words(words, puzzles), result);
}
}
// Accepted solution for LeetCode #1178: Number of Valid Words for Each Puzzle
function findNumOfValidWords(words: string[], puzzles: string[]): number[] {
const cnt: Map<number, number> = new Map();
for (const w of words) {
let mask = 0;
for (const c of w) {
mask |= 1 << (c.charCodeAt(0) - 97);
}
cnt.set(mask, (cnt.get(mask) || 0) + 1);
}
const ans: number[] = [];
for (const p of puzzles) {
let mask = 0;
for (const c of p) {
mask |= 1 << (c.charCodeAt(0) - 97);
}
let x = 0;
const i = p.charCodeAt(0) - 97;
for (let j = mask; j; j = (j - 1) & mask) {
if ((j >> i) & 1) {
x += cnt.get(j) || 0;
}
}
ans.push(x);
}
return ans;
}
Use this to step through a reusable interview workflow for this problem.
Sort the array in O(n log n), then scan for the missing or unique element by comparing adjacent pairs. Sorting requires O(n) auxiliary space (or O(1) with in-place sort but O(n log n) time remains). The sort step dominates.
Bitwise operations (AND, OR, XOR, shifts) are O(1) per operation on fixed-width integers. A single pass through the input with bit operations gives O(n) time. The key insight: XOR of a number with itself is 0, which eliminates duplicates without extra space.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.