LeetCode #127 单词接龙
题目描述
给定两个单词(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" 不在字典中,所以无法进行转换。
方法一:BFS
# BFS标准套路
for 找head
while queue:
for 找邻居
if 没有重复:处理,标记,入队
在广搜时需要访问单词的所有邻接点,可以使用一个列表字典来保存通用状态的所有匹配单词。如 dog
有三个通用状态:*og
、d*g
、do*
,*og
这个状态就可以匹配 aog
、bog
等单词,这样以 *og
为键,匹配到的单词列表为值,就可以得到 dog
这个单词的所有邻居
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
if not endWord in wordList:
return 0
# defaultdict可以在不存在key的时候直接创建这个key
n, dic = len(beginWord), collections.defaultdict(list)
# 将所有单词的所有通用状态能匹配到的单词记录下来
for word in wordList:
for i in range(n):
dic[word[:i] + "*" + word[i+1:]].append(word)
queue = []
queue.append((beginWord, 1))
visited = set()
visited.add(beginWord)
while queue:
word, step = queue.pop(0)
for i in range(n):
for neightbour in dic[word[:i] + "*" + word[i+1:]]:
if neightbour ==endWord:
return step + 1
if not neightbour in visited:
visited.add(neightbour)
queue.append((neightbour, step + 1))
return 0
- 时间复杂度: O ( m ∗ n ) O(m*n) O(m∗n): m m m 为单词长度, n n n 为单词数量
- 空间复杂度: O ( m ∗ n ) O(m*n) O(m∗n)
方法二:双向BFS
思路和BFS基本上一样,就是要从两头一起开始,因为一个单词被访问时的步长就等于从某一头开始变换到这个单词需要的最短步长,所以当从另一头开始的访问列表中出现了这个单词,就可以把从当前的步长加上从另一头访问这个单词的步长,队列退出条件就变成了一个单词同时在两边的访问队列都出现了
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
if not endWord in wordList:
return 0
n, dic = len(beginWord), collections.defaultdict(list)
for word in wordList:
for i in range(n):
dic[word[:i] + "*" + word[i+1:]].append(word)
beginQueue = []
beginQueue.append((beginWord, 1))
# 记录下变换到每个单词的最短步长
beginVisited = collections.defaultdict()
beginVisited[beginWord] = 1
endQueue = []
endQueue.append((endWord, 1))
# 记录下变换到每个单词的最短步长
endVisited = collections.defaultdict()
endVisited[endWord] = 1
while beginQueue and endQueue:
word, step = beginQueue.pop(0)
for i in range(n):
for neightbour in dic[word[:i] + "*" + word[i+1:]]:
if neightbour in endVisited:
return step + endVisited[neightbour]
if not neightbour in beginVisited:
beginVisited[neightbour] = step + 1
beginQueue.append((neightbour, step + 1))
word, step = endQueue.pop(0)
for i in range(n):
for neightbour in dic[word[:i] + "*" + word[i+1:]]:
if neightbour in beginVisited:
return step + beginVisited[neightbour]
if not neightbour in endVisited:
endVisited[neightbour] = step + 1
endQueue.append((neightbour, step + 1))
return 0
- 时间复杂度: O ( m ∗ n ) O(m*n) O(m∗n):和单向BFS一样,但是时间会缩短一半,因为两个搜索会在中间某处相遇
- 空间复杂度: O ( m ∗ n ) O(m*n) O(m∗n)