题目:单词接龙
给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规:每次转换只能改变一个字母。转换过程中的中间单词必须是字典中的单词。
说明:
- 如果不存在这样的转换序列,返回 0。
- 所有单词具有相同的长度。
- 所有单词只由小写字母组成。
- 字典中不存在重复的单词。
- 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。
示例 :
输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
输出: 5
解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", 返回它的长度 5。
输入:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
输出: 0
解释: endWord "cog" 不在字典中,所以无法进行转换。
------------------------------------------------------------------------------------------
思路:本题需要用到广度优先搜索(BFS,broad first search)。
广度优先搜索BFS(Breadth First Search)也称为宽度优先搜索,它是一种先生成的结点先扩展的策略。
(想了解广度优先搜索细节的小伙伴,可以查看:https://blog.csdn.net/weixin_42077402/article/details/94458188)
解法1# : BFS+双向队列(deque)
class Solution(object):
def ladderLength(self, beginWord, endWord, wordList):
"""
:type beginWord: str
:type endWord: str
:type wordList: List[str]
:rtype: int
"""
if endWord not in wordList or beginWord == endWord:
return 0
visited = set() # 储存遍历过符合要求的元素,用于检验是否有效
wordList = set(wordList) # 去重
from collections import deque
q = deque()
q.append([beginWord, 0])
letters = "abcdefghigklmnopqrstuvwsyz"
while q:
# 从队列获取最左边一个元素(current,count),并在队列中删除
# current为当前单词,count表示从beginWord转换到current的步数
current, count = q.popleft()
if current == endWord:
return count+1
for i in range(len(current)):
for j in range(26):
word = current[:i] + letters[j] + current[i+1:]
# 如果变换后的word不在wordList中,或者word之前已经遍历到加到visited中了
if word in wordList and word not in visited:
visited.add(word)
q.append([word, count+1])
return 0 # 遍历没找到
解法2# : 既然可以从beginWord往endWord靠拢,那是否可以反向实现?当然可以。如果两边都可行,是否可以从两头向中间汇集,Binggo!
另外,我们不使用visited
记录访问过的元素,而是通过对已访问过的元素直接赋0
的方式,因为我们知道已访问过的元素,我们不会再访问了。这是一步非常强力的优化。
class Solution(object):
def ladderLength(self, beginWord, endWord, wordList):
"""
:type beginWord: str
:type endWord: str
:type wordList: List[str]
:rtype: int
"""
if endWord not in wordList or beginWord == endWord:
return 0
wordList = set(wordList) # 去重
# 注意因为把没有算头尾的beginWord和endWord,所以这里的count不是从0开始,而是从2开始
# forward_list和backward_list分别为"从前往后"和" 从后往前"的遍历列表
count, forward_list, backward_list = 2, {beginWord}, {endWord}
letters = "abcdefghigklmnopqrstuvwsyz"
while forward_list:
# 每次搜索较短的列表替换给forward_list
if len(forward_list) > len(backward_list):
forward_list, backward_list = backward_list, forward_list
# 每一轮生成一个新的词表
next_list = set()
for forward in forward_list:
for i in range(len(forward)):
for j in range(26):
# 每次替换第i个字母,生成一个新的word
word = forward[:i] + letters[j] + forward[i+1:]
if word in backward_list:
return count
# 符合条件的加入到下一个词表next_list,同时从wordList中剔除
if word in wordList:
next_list.add(word)
wordList.remove(word)
count += 1
# print("next_list:", next_list)
forward_list = next_list # 将下一次的词表赋值给前向遍历词表
return 0 # 遍历没找到
# 这里的
letters = "abcdefghigklmnopqrstuvwsyz"
word = forward[:i] + letters[j] + forward[i+1:]
# 可以替换成:
word = forward[:i] + chr(ord("a")+j) + forward[i+1:]
参考:
https://www.cnblogs.com/cs-whut/p/11147348.html
https://www.runoob.com/python3/python3-ascii-character.html