一、 题目
1. 题目描述
字典 wordList
中从单词 beginWord
和 endWord
的 转换序列 是一个按下述规格形成的序列 beginWord -> s1 -> s2 -> ... -> sk
:
- 每一对相邻的单词只差一个字母。
- 对于
1 <= i <= k
时,每个si
都在wordList
中。注意,beginWord
不需要在wordList
中。 sk == endWord
给你两个单词 beginWord
和 endWord
和一个字典 wordList
,返回 从 beginWord
到 endWord
的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0
。
示例 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” 不在字典中,所以无法进行转换。
提示:
1 <= beginWord.length <= 10
endWord.length == beginWord.length
1 <= wordList.length <= 5000
wordList[i].length == beginWord.length
beginWord
、endWord
和wordList[i]
由小写英文字母组成beginWord != endWord
wordList
中的所有字符串 互不相同
- 广度优先搜索
- 哈希表
- 字符串
- 👍 1055
- 👎 0
2. 原题链接
链接: 127. 单词接龙
二、 解题报告
1. 思路分析
本题状态需要在wordlist内转化,且每次只能改变一个单词,因此可以优化建图,在关系中插入虚拟节点。
例如:abc指向虚拟节点 b_c,a_b,ab_。
- 朴素BFS是可以的,状态就是变化后的单词,每次操作从单词里遍历所有位置选一个字母变成25个其他小写字母,所以状态转化单次操作数为n*26。当然不在wordlist里的剪掉。题目要求endword必须在单词列表里,可以先判。
- 单词状态回溯建,实测有一定优化
312ms
。
- 单词状态回溯建,实测有一定优化
- 然后试了一下双向BFS,巨量优化,达到
72ms
,Python超过100%。 - 看了官方题解,可以优化建图:
- 核心是在wordlist中对每个单词,变化每个字符为’',来作为虚拟节点,所有节点都连接虚拟节点,这样就可以建好连通路径,避免每个单词都进行n26的状态搜索。
- 由于每两个点中间都有虚拟节点,因此最终路径要除以2。
- 测试优化建图后朴素层先BFS速度不错
136ms
。
- 然后试了下优化建图+双向BFS,92ms。
这题由于wordlist的存在,每层状态剪枝很多,所以朴素做法效果不错。
我琢磨最通用的做法应该还是优化建图+双层BFS,普遍性会高一些。
2. 复杂度分析
最坏时间复杂度O(n×c)。
3. 代码实现
优化建图+双向bfs
。92ms
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
abc = set(wordList)
if endWord not in abc:
return 0
n = len(beginWord)
graph = defaultdict(list)
def add_egde(word):
cs = list(word)
for i in range(n):
t = cs[i]
cs[i] = '*'
v_word = ''.join(cs)
graph[v_word].append(word)
graph[word].append(v_word)
abc.add(v_word)
cs[i] = t
for word in wordList:
add_egde(word)
add_egde(beginWord)
v1 = {beginWord:0}
v2 = {endWord:0}
q1 = deque(v1)
q2 = deque(v2)
def bfs(q,v1,v2):
for _ in range(len(q)):
u = q.popleft()
step = v1[u]
for v in graph[u]:
if v in v2:
return (step+1+v2[v])//2+1
if v not in v1:
v1[v] = step+1
q.append(v)
return 0
while q1 and q2:
ret = bfs(q1,v1,v2) if len(q1) < len(q2) else bfs(q2,v2,v1)
# print(v1,v2)
if ret != 0:
return ret
return 0
优化建图+朴素层先法
。136ms
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
abc = set(wordList)
if endWord not in abc:
return 0
n = len(beginWord)
graph = defaultdict(list)
def add_egde(word):
cs = list(word)
for i in range(n):
t = cs[i]
cs[i] = '*'
v_word = ''.join(cs)
graph[v_word].append(word)
graph[word].append(v_word)
abc.add(v_word)
cs[i] = t
for word in wordList:
add_egde(word)
add_egde(beginWord)
visited = {beginWord}
step = 0
q = deque(visited)
while q:
new_q = deque()
step += 1
while q:
u = q.popleft()
for v in graph[u]:
if v == endWord:
return step//2+1
if v not in visited:
visited.add(v)
new_q.append(v)
q =new_q
return 0
双向BFS
。72ms
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
abc = set(wordList)
if endWord not in abc:
return 0
n = len(beginWord)
v1 = {beginWord:1}
v2 = {endWord:0}
q1 = deque(v1)
q2 = deque(v2)
def bfs(q,v1,v2):
for _ in range(len(q)):
x = q.popleft()
step = v1[x] + 1
cur = list(x)
for i in range(n) :
t = cur[i]
for c in 'abcdefghijklmnopqrstuvwxyz':
cur[i] = c
nxt = ''.join(cur)
if nxt in v2:
return step+v2[nxt]
if nxt in abc and nxt not in v1:
v1[nxt] = step
q.append(nxt)
cur[i] = t
return -1
while q1 and q2:
ret = bfs(q1,v1,v2) if len(q1) < len(q2) else bfs(q2,v2,v1)
# print(v1,v2)
if ret != -1:
return ret
return 0
朴素层先法BFS
。312ms
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
abc = set(wordList)
if endWord not in abc:
return 0
n = len(beginWord)
visited = {beginWord}
q =deque(visited)
step = 1
while q:
# print(q,step)
new_q = deque()
step += 1
while q:
x = list(q.popleft())
# nxt = x[:]
for i in range(n) :
t = x[i]
for c in 'abcdefghijklmnopqrstuvwxyz':
x[i] = c
nxt = ''.join(x)
if nxt == endWord:
return step
if nxt in abc and nxt not in visited:
visited.add(nxt)
new_q.append(nxt)
x[i] = t
q = new_q
return 0
三、 本题小结
- 优化见图的思路想法需要多做题多思考。
- 继续练习双向BFS。