Mutating counts without cleanup
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.
Move from brute-force thinking to an efficient approach using hash map strategy.
Design a data structure that is initialized with a list of different words. Provided a string, you should determine if you can change exactly one character in this string to match any word in the data structure.
Implement the MagicDictionary class:
MagicDictionary() Initializes the object.void buildDict(String[] dictionary) Sets the data structure with an array of distinct strings dictionary.bool search(String searchWord) Returns true if you can change exactly one character in searchWord to match any string in the data structure, otherwise returns false.Example 1:
Input
["MagicDictionary", "buildDict", "search", "search", "search", "search"]
[[], [["hello", "leetcode"]], ["hello"], ["hhllo"], ["hell"], ["leetcoded"]]
Output
[null, null, false, true, false, false]
Explanation
MagicDictionary magicDictionary = new MagicDictionary();
magicDictionary.buildDict(["hello", "leetcode"]);
magicDictionary.search("hello"); // return False
magicDictionary.search("hhllo"); // We can change the second 'h' to 'e' to match "hello" so we return True
magicDictionary.search("hell"); // return False
magicDictionary.search("leetcoded"); // return False
Constraints:
1 <= dictionary.length <= 1001 <= dictionary[i].length <= 100dictionary[i] consists of only lower-case English letters.dictionary are distinct.1 <= searchWord.length <= 100searchWord consists of only lower-case English letters.buildDict will be called only once before search.100 calls will be made to search.Problem summary: Design a data structure that is initialized with a list of different words. Provided a string, you should determine if you can change exactly one character in this string to match any word in the data structure. Implement the MagicDictionary class: MagicDictionary() Initializes the object. void buildDict(String[] dictionary) Sets the data structure with an array of distinct strings dictionary. bool search(String searchWord) Returns true if you can change exactly one character in searchWord to match any string in the data structure, otherwise returns false.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Hash Map · Design · Trie
["MagicDictionary", "buildDict", "search", "search", "search", "search"] [[], [["hello","leetcode"]], ["hello"], ["hhllo"], ["hell"], ["leetcoded"]]
implement-trie-prefix-tree)longest-word-in-dictionary)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #676: Implement Magic Dictionary
class Trie {
private Trie[] children = new Trie[26];
private boolean isEnd;
public void insert(String w) {
Trie node = this;
for (char c : w.toCharArray()) {
int i = c - 'a';
if (node.children[i] == null) {
node.children[i] = new Trie();
}
node = node.children[i];
}
node.isEnd = true;
}
public boolean search(String w) {
return dfs(w, 0, this, 0);
}
private boolean dfs(String w, int i, Trie node, int diff) {
if (i == w.length()) {
return diff == 1 && node.isEnd;
}
int j = w.charAt(i) - 'a';
if (node.children[j] != null) {
if (dfs(w, i + 1, node.children[j], diff)) {
return true;
}
}
if (diff == 0) {
for (int k = 0; k < 26; k++) {
if (k != j && node.children[k] != null) {
if (dfs(w, i + 1, node.children[k], 1)) {
return true;
}
}
}
}
return false;
}
}
class MagicDictionary {
private Trie trie = new Trie();
public MagicDictionary() {
}
public void buildDict(String[] dictionary) {
for (String w : dictionary) {
trie.insert(w);
}
}
public boolean search(String searchWord) {
return trie.search(searchWord);
}
}
/**
* Your MagicDictionary object will be instantiated and called as such:
* MagicDictionary obj = new MagicDictionary();
* obj.buildDict(dictionary);
* boolean param_2 = obj.search(searchWord);
*/
// Accepted solution for LeetCode #676: Implement Magic Dictionary
type Trie struct {
children [26]*Trie
isEnd bool
}
func NewTrie() *Trie {
return &Trie{}
}
func (t *Trie) Insert(w string) {
node := t
for _, c := range w {
i := c - 'a'
if node.children[i] == nil {
node.children[i] = NewTrie()
}
node = node.children[i]
}
node.isEnd = true
}
func (t *Trie) Search(w string) bool {
var dfs func(int, *Trie, int) bool
dfs = func(i int, node *Trie, diff int) bool {
if i >= len(w) {
return diff == 1 && node.isEnd
}
j := int(w[i] - 'a')
if node.children[j] != nil && dfs(i+1, node.children[j], diff) {
return true
}
if diff == 0 {
for k := 0; k < 26; k++ {
if k != j && node.children[k] != nil && dfs(i+1, node.children[k], 1) {
return true
}
}
}
return false
}
return dfs(0, t, 0)
}
type MagicDictionary struct {
trie *Trie
}
func Constructor() MagicDictionary {
return MagicDictionary{trie: NewTrie()}
}
func (md *MagicDictionary) BuildDict(dictionary []string) {
for _, w := range dictionary {
md.trie.Insert(w)
}
}
func (md *MagicDictionary) Search(searchWord string) bool {
return md.trie.Search(searchWord)
}
/**
* Your MagicDictionary object will be instantiated and called as such:
* obj := Constructor();
* obj.BuildDict(dictionary);
* param_2 := obj.Search(searchWord);
*/
# Accepted solution for LeetCode #676: Implement Magic Dictionary
class Trie:
__slots__ = ["children", "is_end"]
def __init__(self):
self.children = {}
self.is_end = False
def insert(self, w: str) -> None:
node = self
for c in w:
if c not in node.children:
node.children[c] = Trie()
node = node.children[c]
node.is_end = True
def search(self, w: str) -> bool:
def dfs(i: int, node: Trie, diff: int) -> bool:
if i == len(w):
return diff == 1 and node.is_end
if w[i] in node.children and dfs(i + 1, node.children[w[i]], diff):
return True
return diff == 0 and any(
dfs(i + 1, node.children[c], 1) for c in node.children if c != w[i]
)
return dfs(0, self, 0)
class MagicDictionary:
def __init__(self):
self.trie = Trie()
def buildDict(self, dictionary: List[str]) -> None:
for w in dictionary:
self.trie.insert(w)
def search(self, searchWord: str) -> bool:
return self.trie.search(searchWord)
# Your MagicDictionary object will be instantiated and called as such:
# obj = MagicDictionary()
# obj.buildDict(dictionary)
# param_2 = obj.search(searchWord)
// Accepted solution for LeetCode #676: Implement Magic Dictionary
struct Trie {
children: [Option<Box<Trie>>; 26],
is_end: bool,
}
impl Trie {
fn new() -> Self {
Trie {
children: Default::default(),
is_end: false,
}
}
fn insert(&mut self, w: &str) {
let mut node = self;
for c in w.chars() {
let i = (c as usize) - ('a' as usize);
if node.children[i].is_none() {
node.children[i] = Some(Box::new(Trie::new()));
}
node = node.children[i].as_mut().unwrap();
}
node.is_end = true;
}
fn search(&self, w: &str) -> bool {
self.dfs(w, 0, 0)
}
fn dfs(&self, w: &str, i: usize, diff: usize) -> bool {
if i == w.len() {
return diff == 1 && self.is_end;
}
let j = (w.chars().nth(i).unwrap() as usize) - ('a' as usize);
if let Some(child) = &self.children[j] {
if child.dfs(w, i + 1, diff) {
return true;
}
}
if diff == 0 {
for k in 0..26 {
if k != j {
if let Some(child) = &self.children[k] {
if child.dfs(w, i + 1, 1) {
return true;
}
}
}
}
}
false
}
}
struct MagicDictionary {
trie: Trie,
}
impl MagicDictionary {
fn new() -> Self {
MagicDictionary { trie: Trie::new() }
}
fn build_dict(&mut self, dictionary: Vec<String>) {
for w in dictionary {
self.trie.insert(&w);
}
}
fn search(&self, search_word: String) -> bool {
self.trie.search(&search_word)
}
}
// Accepted solution for LeetCode #676: Implement Magic Dictionary
class Trie {
private children: Trie[] = Array(26).fill(null);
private isEnd: boolean = false;
constructor() {}
insert(w: string): void {
let node: Trie = this;
for (const c of w) {
const i: number = c.charCodeAt(0) - 'a'.charCodeAt(0);
if (!node.children[i]) {
node.children[i] = new Trie();
}
node = node.children[i];
}
node.isEnd = true;
}
search(w: string): boolean {
const dfs = (i: number, node: Trie, diff: number): boolean => {
if (i >= w.length) {
return diff === 1 && node.isEnd;
}
const j: number = w.charCodeAt(i) - 'a'.charCodeAt(0);
if (node.children[j] && dfs(i + 1, node.children[j], diff)) {
return true;
}
if (diff === 0) {
for (let k = 0; k < 26; k++) {
if (k !== j && node.children[k] && dfs(i + 1, node.children[k], 1)) {
return true;
}
}
}
return false;
};
return dfs(0, this, 0);
}
}
class MagicDictionary {
private trie: Trie;
constructor() {
this.trie = new Trie();
}
buildDict(dictionary: string[]): void {
for (const w of dictionary) {
this.trie.insert(w);
}
}
search(searchWord: string): boolean {
return this.trie.search(searchWord);
}
}
/**
* Your MagicDictionary object will be instantiated and called as such:
* var obj = new MagicDictionary()
* obj.buildDict(dictionary)
* var param_2 = obj.search(searchWord)
*/
Use this to step through a reusable interview workflow for this problem.
Use a simple list or array for storage. Each operation (get, put, remove) requires a linear scan to find the target element — O(n) per operation. Space is O(n) to store the data. The linear search makes this impractical for frequent operations.
Design problems target O(1) amortized per operation by combining data structures (hash map + doubly-linked list for LRU, stack + min-tracking for MinStack). Space is always at least O(n) to store the data. The challenge is achieving constant-time operations through clever structure composition.
Review these before coding to avoid predictable interview regressions.
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.