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.
Move from brute-force thinking to an efficient approach using array strategy.
Given a wordlist, we want to implement a spellchecker that converts a query word into a correct word.
For a given query word, the spell checker handles two categories of spelling mistakes:
wordlist = ["yellow"], query = "YellOw": correct = "yellow"wordlist = ["Yellow"], query = "yellow": correct = "Yellow"wordlist = ["yellow"], query = "yellow": correct = "yellow"('a', 'e', 'i', 'o', 'u') of the query word with any vowel individually, it matches a word in the wordlist (case-insensitive), then the query word is returned with the same case as the match in the wordlist.
wordlist = ["YellOw"], query = "yollow": correct = "YellOw"wordlist = ["YellOw"], query = "yeellow": correct = "" (no match)wordlist = ["YellOw"], query = "yllw": correct = "" (no match)In addition, the spell checker operates under the following precedence rules:
Given some queries, return a list of words answer, where answer[i] is the correct word for query = queries[i].
Example 1:
Input: wordlist = ["KiTe","kite","hare","Hare"], queries = ["kite","Kite","KiTe","Hare","HARE","Hear","hear","keti","keet","keto"] Output: ["kite","KiTe","KiTe","Hare","hare","","","KiTe","","KiTe"]
Example 2:
Input: wordlist = ["yellow"], queries = ["YellOw"] Output: ["yellow"]
Constraints:
1 <= wordlist.length, queries.length <= 50001 <= wordlist[i].length, queries[i].length <= 7wordlist[i] and queries[i] consist only of only English letters.Problem summary: Given a wordlist, we want to implement a spellchecker that converts a query word into a correct word. For a given query word, the spell checker handles two categories of spelling mistakes: Capitalization: If the query matches a word in the wordlist (case-insensitive), then the query word is returned with the same case as the case in the wordlist.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Hash Map
["KiTe","kite","hare","Hare"] ["kite","Kite","KiTe","Hare","HARE","Hear","hear","keti","keet","keto"]
["yellow"] ["YellOw"]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #966: Vowel Spellchecker
class Solution {
public String[] spellchecker(String[] wordlist, String[] queries) {
Set<String> s = new HashSet<>();
Map<String, String> low = new HashMap<>();
Map<String, String> pat = new HashMap<>();
for (String w : wordlist) {
s.add(w);
String t = w.toLowerCase();
low.putIfAbsent(t, w);
pat.putIfAbsent(f(t), w);
}
int m = queries.length;
String[] ans = new String[m];
for (int i = 0; i < m; ++i) {
String q = queries[i];
if (s.contains(q)) {
ans[i] = q;
continue;
}
q = q.toLowerCase();
if (low.containsKey(q)) {
ans[i] = low.get(q);
continue;
}
q = f(q);
if (pat.containsKey(q)) {
ans[i] = pat.get(q);
continue;
}
ans[i] = "";
}
return ans;
}
private String f(String w) {
char[] cs = w.toCharArray();
for (int i = 0; i < cs.length; ++i) {
char c = cs[i];
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
cs[i] = '*';
}
}
return String.valueOf(cs);
}
}
// Accepted solution for LeetCode #966: Vowel Spellchecker
func spellchecker(wordlist []string, queries []string) (ans []string) {
s := map[string]bool{}
low := map[string]string{}
pat := map[string]string{}
f := func(w string) string {
res := []byte(w)
for i := range res {
if res[i] == 'a' || res[i] == 'e' || res[i] == 'i' || res[i] == 'o' || res[i] == 'u' {
res[i] = '*'
}
}
return string(res)
}
for _, w := range wordlist {
s[w] = true
t := strings.ToLower(w)
if _, ok := low[t]; !ok {
low[t] = w
}
if _, ok := pat[f(t)]; !ok {
pat[f(t)] = w
}
}
for _, q := range queries {
if s[q] {
ans = append(ans, q)
continue
}
q = strings.ToLower(q)
if s, ok := low[q]; ok {
ans = append(ans, s)
continue
}
q = f(q)
if s, ok := pat[q]; ok {
ans = append(ans, s)
continue
}
ans = append(ans, "")
}
return
}
# Accepted solution for LeetCode #966: Vowel Spellchecker
class Solution:
def spellchecker(self, wordlist: List[str], queries: List[str]) -> List[str]:
def f(w):
t = []
for c in w:
t.append("*" if c in "aeiou" else c)
return "".join(t)
s = set(wordlist)
low, pat = {}, {}
for w in wordlist:
t = w.lower()
low.setdefault(t, w)
pat.setdefault(f(t), w)
ans = []
for q in queries:
if q in s:
ans.append(q)
continue
q = q.lower()
if q in low:
ans.append(low[q])
continue
q = f(q)
if q in pat:
ans.append(pat[q])
continue
ans.append("")
return ans
// Accepted solution for LeetCode #966: Vowel Spellchecker
use std::collections::{HashMap, HashSet};
impl Solution {
pub fn spellchecker(wordlist: Vec<String>, queries: Vec<String>) -> Vec<String> {
let s: HashSet<String> = wordlist.iter().cloned().collect();
let mut low: HashMap<String, String> = HashMap::new();
let mut pat: HashMap<String, String> = HashMap::new();
let f = |w: &str| -> String {
w.chars()
.map(|c| match c {
'a' | 'e' | 'i' | 'o' | 'u' => '*',
_ => c,
})
.collect()
};
for w in &wordlist {
let mut t = w.to_lowercase();
if !low.contains_key(&t) {
low.insert(t.clone(), w.clone());
}
t = f(&t);
if !pat.contains_key(&t) {
pat.insert(t.clone(), w.clone());
}
}
let mut ans: Vec<String> = Vec::new();
for query in queries {
if s.contains(&query) {
ans.push(query);
continue;
}
let mut q = query.to_lowercase();
if let Some(v) = low.get(&q) {
ans.push(v.clone());
continue;
}
q = f(&q);
if let Some(v) = pat.get(&q) {
ans.push(v.clone());
continue;
}
ans.push("".to_string());
}
ans
}
}
// Accepted solution for LeetCode #966: Vowel Spellchecker
function spellchecker(wordlist: string[], queries: string[]): string[] {
const s = new Set(wordlist);
const low = new Map<string, string>();
const pat = new Map<string, string>();
const f = (w: string): string => {
let res = '';
for (const c of w) {
if ('aeiou'.includes(c)) {
res += '*';
} else {
res += c;
}
}
return res;
};
for (const w of wordlist) {
let t = w.toLowerCase();
if (!low.has(t)) {
low.set(t, w);
}
t = f(t);
if (!pat.has(t)) {
pat.set(t, w);
}
}
const ans: string[] = [];
for (let q of queries) {
if (s.has(q)) {
ans.push(q);
continue;
}
q = q.toLowerCase();
if (low.has(q)) {
ans.push(low.get(q)!);
continue;
}
q = f(q);
if (pat.has(q)) {
ans.push(pat.get(q)!);
continue;
}
ans.push('');
}
return ans;
}
Use this to step through a reusable interview workflow for this problem.
Two nested loops check every pair or subarray. The outer loop fixes a starting point, the inner loop extends or searches. For n elements this gives up to n²/2 operations. No extra space, but the quadratic time is prohibitive for large inputs.
Most array problems have an O(n²) brute force (nested loops) and an O(n) optimal (single pass with clever state tracking). The key is identifying what information to maintain as you scan: a running max, a prefix sum, a hash map of seen values, or two pointers.
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.