127.单词接龙
难度:中等
标签:双向BFS
题目描述
给定两个单词(beginWord
和 endWord
)和一个字典,找到从 beginWord
到 endWord
的最短转换序列的长度。转换需遵循如下规则:
- 每次转换只能改变一个字母。
- 转换过程中的中间单词必须是字典中的单词。
说明:
- 如果不存在这样的转换序列,返回 0。
- 所有单词具有相同的长度。
- 所有单词只由小写字母组成。
- 字典中不存在重复的单词。
- 你可以假设
beginWord
和endWord
是非空的,且二者不相同。
示例 1:
输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
输出: 5
解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog",
返回它的长度 5。
示例 2:
输入:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
输出: 0
解释: endWord "cog" 不在字典中,所以无法进行转换。
题解
对于本题来说,从beginWord
到endWord
与从endWord
到beginWord
最终得到的结果是一样的,可以分别从两端构造一个树,树的结点是转换过程中的中间节点,然后利用BFS进行搜索,找到最短路径长度,也就是树的最小高度。对于BFS来说,复杂度与树的每一层的结点数量相关,为了加速查找,可以采取两端搜索的方法来进行优化,也就是说一端从beginWord
到endWord
进行查找,另一端从endWord
到endWord
进行查找,每次BFS遍历中间结点个数少的那一端。
举个例子:
- 假设从
beginWord
转换为endWord
,存在于字典中的,(第一个)中间结果有30
个。 - 而,从
endWord
转换为beginWord
,存在于字典中的,(第一个)中间结果只有2
个。 - 那么,很显然。从
endWord
开始会更快。所以,每次都从个数少的那块开始替换一位。
代码
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
if endWord not in wordList:
return 0
l = len(endWord)
ws = set(wordList)
head = {beginWord}
tail = {endWord}
tmp = list('abcdefghijklmnopqrstuvwxyz')
res = 1
while head:
if len(head) > len(tail):
head, tail = tail, head
q = set()
for cur in head:
for i in range(l):
for j in tmp:
word = cur[:i] + j + cur[i+1:]
if word in tail:
return res + 1
if word in ws:
q.add(word)
ws.remove(word)
head = q
res += 1
return 0
总结
对于一个BFS问题来说,一般都具备几个要素:
-
需要有边界条件:边界条件可能为迷宫的墙壁和边缘,也可能为题目中给出的不可用的数据
-
有已访问数组:当前位置未被访问是能够入队列的必要条件
-
可能有前驱节点:用于需要输出路径时添加
-
需要队列来存储当前层的节点
双向BFS核心技巧:使用两个队列进行BFS,每次只遍历队列较短的一侧