实现一个魔法字典 图解题解
这道题到底在问什么
- 输入
- buildDict(["hello","world","leetcode"]) search("hhllo")
- 输出
- true(hhllo 把第 2 位 h 改成 e 就成 hello)
- 输入
- search("hello")
- 输出
- false(它本身就在字典里,0 处改动;本题要求恰好改 1 处)
最优解:一步一步想明白
- 3核心就一个计数:逐位比较,数不同的位数 diff。diff==1 即命中。下面把每一位的比较演给你看。
- 4【查询 "hhllo" · 候选 1/2】格子里是候选词「world」,要和查询词「hhllo」一位一位比。diff 记录目前为止有几位不同,从 0 开始。
- 5指针走到第 0 位。查询词这位是 'h',候选这位是 'w',两者不同,diff 要加一。
- 6这位不同,diff 加一变成 1(红色是不同的位)。继续比下一位。
- 7指针走到第 1 位。查询词这位是 'h',候选这位是 'o',两者不同,diff 要加一。
- 8这位不同,diff 加一变成 2(红色是不同的位)。继续比下一位。
- 9指针走到第 2 位。查询词这位是 'l',候选这位是 'r',两者不同,diff 要加一。
- 10这位不同,diff 加一变成 3(红色是不同的位)。继续比下一位。
- 11指针走到第 3 位。查询词这位是 'l',候选这位是 'l',两者相同,diff 不变。
- 12这位相同,diff 保持 3(绿色是到目前为止相同的位)。继续比下一位。
- 13指针走到第 4 位。查询词这位是 'o',候选这位是 'd',两者不同,diff 要加一。
- 14这位不同,diff 加一变成 4(红色是不同的位)。继续比下一位。
- 15比完整词,一共 4 位不同。diff 不等于 1,改一处变不成「world」,换下一个候选继续试。
- 16【查询 "hhllo" · 候选 2/2】格子里是候选词「hello」,要和查询词「hhllo」一位一位比。diff 记录目前为止有几位不同,从 0 开始。
- 17指针走到第 0 位。查询词这位是 'h',候选这位是 'h',两者相同,diff 不变。
- 18这位相同,diff 保持 0(绿色是到目前为止相同的位)。继续比下一位。
- 19指针走到第 1 位。查询词这位是 'h',候选这位是 'e',两者不同,diff 要加一。
- 20这位不同,diff 加一变成 1(红色是不同的位)。继续比下一位。
- 21指针走到第 2 位。查询词这位是 'l',候选这位是 'l',两者相同,diff 不变。
- 22这位相同,diff 保持 1(绿色是到目前为止相同的位)。继续比下一位。
- 23指针走到第 3 位。查询词这位是 'l',候选这位是 'l',两者相同,diff 不变。
- 24这位相同,diff 保持 1(绿色是到目前为止相同的位)。继续比下一位。
- 25指针走到第 4 位。查询词这位是 'o',候选这位是 'o',两者相同,diff 不变。
- 26这位相同,diff 保持 1(绿色是到目前为止相同的位)。继续比下一位。
- 27比完整词,一共 1 位不同。diff 恰好等于 1,说明只改那一个红色位置就能变成「hello」——命中,search 返回 true。
- 28【查询 "world" · 候选 1/2】格子里是候选词「hello」,要和查询词「world」一位一位比。diff 记录目前为止有几位不同,从 0 开始。
- 29指针走到第 0 位。查询词这位是 'w',候选这位是 'h',两者不同,diff 要加一。
- 30这位不同,diff 加一变成 1(红色是不同的位)。继续比下一位。
- 31指针走到第 1 位。查询词这位是 'o',候选这位是 'e',两者不同,diff 要加一。
- 32这位不同,diff 加一变成 2(红色是不同的位)。继续比下一位。
- 33指针走到第 2 位。查询词这位是 'r',候选这位是 'l',两者不同,diff 要加一。
- 34这位不同,diff 加一变成 3(红色是不同的位)。继续比下一位。
- 35指针走到第 3 位。查询词这位是 'l',候选这位是 'l',两者相同,diff 不变。
- 36这位相同,diff 保持 3(绿色是到目前为止相同的位)。继续比下一位。
- 37指针走到第 4 位。查询词这位是 'd',候选这位是 'o',两者不同,diff 要加一。
- 38这位不同,diff 加一变成 4(红色是不同的位)。继续比下一位。
- 39比完整词,一共 4 位不同。diff 不等于 1,改一处变不成「hello」,换下一个候选继续试。
- 40【查询 "world" · 候选 2/2】格子里是候选词「world」,要和查询词「world」一位一位比。diff 记录目前为止有几位不同,从 0 开始。
- 41指针走到第 0 位。查询词这位是 'w',候选这位是 'w',两者相同,diff 不变。
- 42这位相同,diff 保持 0(绿色是到目前为止相同的位)。继续比下一位。
- 43指针走到第 1 位。查询词这位是 'o',候选这位是 'o',两者相同,diff 不变。
- 44这位相同,diff 保持 0(绿色是到目前为止相同的位)。继续比下一位。
- 45指针走到第 2 位。查询词这位是 'r',候选这位是 'r',两者相同,diff 不变。
- 46这位相同,diff 保持 0(绿色是到目前为止相同的位)。继续比下一位。
- 47指针走到第 3 位。查询词这位是 'l',候选这位是 'l',两者相同,diff 不变。
- 48这位相同,diff 保持 0(绿色是到目前为止相同的位)。继续比下一位。
- 49指针走到第 4 位。查询词这位是 'd',候选这位是 'd',两者相同,diff 不变。
- 50这位相同,diff 保持 0(绿色是到目前为止相同的位)。继续比下一位。
- 51比完整词,一共 0 位不同。diff 不等于 1,改一处变不成「world」,换下一个候选继续试。
- 52查询 "world" 时:和 hello 比 diff=4,和它自己比 diff=0(全绿、一处没改)。没有任何候选的 diff 等于 1,所以恰好改一处变不出新词,返回 false。
⚠️ 容易写错的地方
✗ 错:把 diff==0(原词就在字典里)也判成 true
✓ 对:必须 diff 恰好等于 1
本题要求「恰好改一个字母」,改 0 个不算,所以原词在字典里也要返回 false
✗ 错:拿不等长的单词去比
✓ 对:先判 len 相等再逐位比
改一个字母不会改变长度,长度不同的单词根本不可能由一次改动得到
✗ 错:找到第一处不同就停、直接返回
✓ 对:比完整个词、数出总 diff 再判断
只看到一处不同不代表只有一处;可能后面还有第二处,那就不满足恰好一个
完整代码(Python / C++ / Java)
Python
class MagicDictionary:
def buildDict(self, words):
self.words = words # 存下所有单词
def search(self, word):
for w in self.words:
if len(w) != len(word): # 只比等长的
continue
diff = sum(a != b for a, b in zip(word, w))
if diff == 1: # 恰好改一处
return True
return FalseC++
class MagicDictionary {
vector<string> ws;
public:
void buildDict(vector<string> words){ ws = words; }
bool search(string word){
for (auto& w : ws) {
if (w.size() != word.size()) continue;
int diff = 0;
for (int i=0;i<w.size();i++) diff += w[i]!=word[i];
if (diff == 1) return true;
}
return false;
}
};Java
class MagicDictionary {
String[] ws;
public void buildDict(String[] words){ ws = words; }
public boolean search(String word){
for (String w : ws) {
if (w.length() != word.length()) continue;
int diff = 0;
for (int i=0;i<w.length();i++)
if (w.charAt(i) != word.charAt(i)) diff++;
if (diff == 1) return true;
}
return false;
}
}复杂度
时间
O(N·L)
N 是字典单词数,L 是单词长度;每次 search 最多把每个同长单词逐位比一遍
空间
O(N·L)
只额外存下字典里的所有单词,比较时不开新数组
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 实现一个魔法字典 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
diff 是什么,命中条件为什么是 diff==1?+
diff 是查询词和某个等长候选逐位比较时「不同的位数」。改一个字母正好让一个位置从不同变相同,所以能由一次改动互相变成的两个词,diff 恰好是 1。
为什么只比长度相同的单词?+
题目是「改一个字母」,不增不删,长度不变。长度不同的单词无论怎么改一个字母都不可能相等,直接跳过省时间。
单词非常多时怎么优化?+
把字典建成字典树(Trie),search 时做 DFS 并维护「已用过几次失配」,沿某条边失配就消耗掉唯一的一次改动机会,把不可能的分支早早剪掉。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄把 实现一个魔法字典 一步步讲透(全站已上线 905 份讲透,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
把这道题真正拿下
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。