SWE / PE · Coding

Meta Word Ladder:单字符变化、BFS与桶扫描

求字典内逐步替换一个字符的最短词链,构造通配桶避免每次比较全部单词。

题目重点:求字典内逐步替换一个字符的最短词链,构造通配桶避免每次比较全部单词。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 训练非空同长度ASCII小写词,每步只替换一个字符,结果计链中词数;end必须在字典,重复词去重。begin=end也仍按end存在前提判断。
  • begin可不在字典,作为起点加入;无路径返回0,不要求所有最短词链。

解题路线

  1. 构造每词L个pattern,例如hot→*ot/h*t/ho*。同pattern词之间只差该位置,因此可作为无权图邻居。*不属于输入字符,避免与真实字符混淆。
  2. BFS保存(word,distance)且入队即seen,第一次遇end的distance为最短词数。起点距离1,不是边数0。
  3. 某pattern第一次被扫描时把其中未见词都入队,随后删除该bucket;所有这些词此时被最早可达层触及,后续单源BFS再扫不会产生更短发现,因此无需反复遍历大桶。
  4. bucket删除适用于只求一次最短长度;若要所有最短路径,需要同层多父记录,不能直接沿用永久删除后假定保留所有路径。双向BFS也需要独立frontier/visited与停止证明。
  5. 输入校验长度和字符,end缺失提前0。词列表大时不要为每个被出队词做O(NL)全字典比较;bucket预处理换取额外空间。

手动走一遍

hit→hot→dot→dog→cog得到5,字典可为hot/dot/dog/lot/log/cog。没有cog则0;若begin=end且字典含该词返回1。

Python 参考实现

下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。

代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。

from collections import deque


def ladder_length(begin,end,words):
    """Same-length lowercase ASCII words, one substitution; length counts words.

    end must be in dictionary even when begin==end. Duplicate entries deduped.
    """
    length=len(begin)
    if len(end)!=length or any(len(w)!=length for w in words):raise ValueError('dimension mismatch')
    if any(not w or any(not 'a'<=c<='z' for c in w) for w in [begin,end,*words]):
        raise ValueError('nonempty lowercase ASCII words')
    dictionary=set(words)
    if end not in dictionary:return 0
    dictionary.add(begin);buckets={}
    for word in dictionary:
        for i in range(length):
            pattern=word[:i]+'*'+word[i+1:];buckets.setdefault(pattern,[]).append(word)
    queue=deque([(begin,1)]);seen={begin}
    while queue:
        word,distance=queue.popleft()
        if word==end:return distance
        for i in range(length):
            pattern=word[:i]+'*'+word[i+1:]
            for neighbor in buckets.pop(pattern,[]):
                if neighbor not in seen:
                    seen.add(neighbor);queue.append((neighbor,distance+1))
    return 0

复杂度

Python创建pattern切片长度L,因此预处理O(NL²)时间、存储字符串pattern最坏O(NL²)字节加O(NL)桶引用。BFS每桶只遍历一次O(NL)邻接引用,但生成pattern仍O(NL²);不能忽略Python切片而直接标O(NL)。

易错点与追问

容易失分的边界

  • visited出队才设置会在同层重复入队多个相同词。
  • 返回边数与题目词数少1。
  • 求所有最短路径时只留单父或提前删桶会漏等长分支。

继续深挖

  • 若字母表很小,直接每位置试26字符查set与pattern桶如何比较?
  • 要返回一条路径时,parent应在何时设置避免覆盖最短发现?
✓已复制微信号!