本文最后更新于:2020年11月5日 晚上
127. 单词接龙
给定两个单词(beginWord和endWord)和一个字典,找到从beginWord到endWord的最短长度,转换需遵循如下规则:
- 每次转换只能改变一个字母。
- 转换过程中的中间单词必须是字典中的单词。
说明:
- 如果不存在这样的转换序列,返回9.
- 所有单词具有相同的长度。
- 所有的单词只由小写字母组成。
- 字典中不存在重复的单词。
- 你可以假设beginWord和endWord是非空的,且二者不相同。
示例1:
输入:
beginWord='hit',
endWord='cog',
wordList=['hot','dot','dog','lot','log','cog']
输出:5
解释:一个最短转换序列是'hit'->'hot'->'dog'->'cog',返回它的长度5.
示例2:
输入:
beginWord='hit'
endWord='cog'
wordList=['hot','dot'.'dog','lot','log']
输出:0
解释:endWord 'cog'不在字典中,所以无法进行转换。
方法一:广度优先搜素+优化建图
思路:
本题要求的是最短转换序列的长度。广度优先搜索。抽象为图的模型。
每个单词抽象为一个点,如果两个单词可以只改变一个字母进行转换,那么说明它们之间有一条双向边,因此我们只需要把满足转换条件的点相连,就形成了一张图。
基于该图,我们以beginWord为图的起点,以endWord为终点进行广度优先搜索,寻找beginWord和endWord的最短路径。
具体地,我们可以创建虚拟节点。对于单词hit,我们创建三个虚拟节*it、h*t、hi*,并让hit向这三个虚拟节点分别连一条边即可。如果一个单词能够转换为hit,那么该单词必然会连接到这三个虚拟节点之一。对于每一个单词,我们枚举它连接到的虚拟节点,把该单词对应的id与这些虚拟节点对应的id相连即可。
最后我们将起点加入队列开始广度优先搜索,当搜索到终点时,我们就找到了最短路径的长度。注意因为添加了虚拟节点,我们得到的距离为实际最短路径长度的两倍。同时我们并未计算起点对答案的贡献,所以我们应当返回距离的一半加一的结果。
代码:
import collections
class Solution:
def ladderLength(self, beginWord, endWord,wordList):
def addWord(word):
if word not in wordId:
nonlocal wordnum
wordId[word] = wordnum
wordnum += 1
def addEdge(word):
addWord(word)
id1 = wordId[word]
chars = list(word)
for i in range(len(chars)):
tmp = chars[i]
chars[i] = '*'
newword = ''.join(chars)
addWord(newword)
id2 = wordId[newword]
edge[id1].append(id2)
edge[id2].append(id1)
chars[i] = tmp
wordId = dict()
edge = collections.defaultdict(list)
wordnum = 0
for word in wordList:
addEdge(word)
addEdge(beginWord)
dis = [float("inf")] * wordnum
if endWord not in wordId:
return 0
beginId, endId = wordId[beginWord], wordId[endWord]
dis[beginId] = 0
que = collections.deque([beginId])
while que:
x = que.popleft()
if x == endId:
return dis[endId] // 2 + 1
for nextnode in edge[x]:
if dis[nextnode] == float("inf"):
dis[nextnode] = dis[x] + 1
que.append(nextnode)
return 0
复杂度分析:
- 时间复杂度:$O(N \times C^2)$????。其中$N$为wordList的长度,$C$为列表中单词的平均长度。
- 建图过程中,对于每一个单词,我们需要枚举它连接到的所有虚拟节点,时间复杂度为$O(C)$,将这些单词加入到哈希表中,时间复杂度为$O(N \times C)$,因此总时间复杂度为$O(N \times C)$。
- 广度优先搜索的时间复杂度最坏情况下是$O(N \times C)$。
- 空间复杂度:$O(N \times C^2)$。其中$N$为wordList的长度,$C$为列表中单词的平均长度。哈希表中包含$O(N \times C)$个节点,每个节点占用空间$O(C)$,因此总的空间复杂度为$O(N \times C)$。
方法二:双向广度优先搜索
思路:
根据给定字典构造的图可能会很大,而广度优先搜索的搜索空间大小依赖于每层节点的分支数量。假如每个节点的分支数量相同,搜索空间会随着层数的增长指数级的增加。
如果使用两个同时进行的广搜可以有效地减少搜索空间。一边从beginWord开始,另一边从endWord开始。我们每次从两个各扩展一层节点,当发现某一时刻两边都访问过同一顶点时就停止搜索。这就是双向广度优先搜索,它可以可观地减少搜索空间,从而提高代码运行效率。
代码:
class Solution:
def ladderLength(self, beginWord, endWord, wordList):
def addWord(word):
if word not in wordId:
nonlocal wordnum
wordId[word] = wordnum
wordnum += 1
def addEdge(word):
addWord(word)
id1 = wordId[word]
chars = list(word)
for i in range(len(chars)):
tmp = chars[i]
chars[i] = '*'
newword = ''.join(chars)
addWord(newword)
id2 = wordId[newword]
edge[id1].append(id2)
edge[id2].append(id1)
chars[i] = tmp
wordnum = 0
wordId = dict()
edge = collections.defaultdict(list)
for word in wordList:
addEdge(word)
addEdge(beginWord)
if endWord not in wordId:
return 0
beginId, endId = wordId[beginWord], wordId[endWord]
quebegin = collections.deque([beginId])
disbegin = [float('inf')] * wordnum
disbegin[beginId] = 0
queend = collections.deque([endId])
disend = [float('inf')] * wordnum
disend[endId] = 0
while quebegin or queend:
quebeginsize = len(quebegin)
for _ in range(quebeginsize):
nodebegin = quebegin.popleft()
if disend[nodebegin] != float('inf'):
return (disbegin[nodebegin] + disend[nodebegin]) // 2 + 1
for nextnode in edge[nodebegin]:
if disbegin[nextnode] == float('inf'):
disbegin[nextnode] = disbegin[nodebegin] + 1
quebegin.append(nextnode)
queendsize = len(queend)
for _ in range(queendsize):
nodeend = queend.popleft()
if disbegin[nodeend] != float('inf'):
return (disbegin[nodeend] + disend[nodeend]) // 2 + 1
for nextnode in edge[nodeend]:
if disend[nextnode] == float('inf'):
disend[nextnode] = disend[nodeend] + 1
queend.append(nextnode)
return 0
复杂度分析:
- 时间复杂度:$O(N \times C^2)$。其中$N$为wordList的长度,$C$为列表中单词的平均长度。
- 建图过程中,对于每一个单词,我们需要枚举它连接到的所有虚拟节点,时间复杂度为$O(C)$,将这些单词加入哈希表中,时间复杂度为$O(N \times C)$,因此总时间复杂度为$O(N \times C)$。
- 双向广度优先搜索的时间复杂度最坏情况下是$O(N \times C)$,因此总时间复杂度为$O(N \times C^2)$。
- 空间复杂度:$O(N \times C^2)$。其中$N$为wordList的长度,$C$为列表中单词的平均长度,哈希表中包含$O(N \times C)$个节点,每个节点占用空间$O(C)$。因此总的空间复杂度为$O(N \times C^2)$。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!