发布于2020-08-01 15:35 阅读(1129) 评论(0) 点赞(24) 收藏(4)
中等
给定两个单词(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" 不在字典中,所以无法进行转换。
首先借助图的思想建立图
将单词中的某一字母换成"*",表示可以为任何字母,比如"hit"可以转化为"*it"、“h*t"和"hi*”
在"h*t"模式中包含了"hit"和"hot",那么可以认为这两个单词之间存在一条边
即只要两个单词相差一个字母,那么这两个单词之间就存在边
单词之间存在边,可以认为是邻居节点
建立如下的图
然后就可以广度优先遍历来获得开始单词和结束单词之间的距离
算法流程
具体看代码
from collections import defaultdict
from typing import List
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
if endWord not in wordList:
return 0
length = len(beginWord)
# 用于保存同一种模式下的单词信息
dic = defaultdict(set)
for word in wordList:
for i in range(length):
dic[word[:i] + "*" + word[i + 1:]].add(word)
# 记录单词层数
word_level = {beginWord: 0}
queue = [beginWord]
while queue:
cur = queue.pop(0)
# 遍历该单词的不同模式
for i in range(length):
# 遍历当前单词的邻居节点
for word in dic[cur[:i] + "*" + cur[i + 1:]]:
# 添加单词的层数记录
if word not in word_level:
word_level[word] = word_level[cur] + 1
queue.append(word)
# 返回结果
if word == endWord:
return word_level[word] + 1
return 0
广度优先遍历有一特点,通常情况下,每一层的节点会比上一次的节点数要多,有着指数爆炸的可能性
一边从起始节点开始向后查找,一边从结束节点开始向前查找
分别建立从起始节点开始查找的节点层数的记录,和从结束节点开始查找的节点层数记录
直到某一节点同时出现在两个记录中,说明找到了一条路径,将两个记录的层数相加即可
class Solution:
def __init__(self):
self.length = 0
self.dic = defaultdict(set)
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
if endWord not in wordList:
return 0
self.length = len(beginWord)
for word in wordList:
for i in range(self.length):
self.dic[word[:i] + "*" + word[i + 1:]].add(word)
word_level_begin = {beginWord: 0}
queue_begin = [beginWord]
word_level_end = {endWord: 0}
queue_end = [endWord]
while queue_begin and queue_end:
ans = self.visit(queue_begin, word_level_begin, word_level_end)
if ans:
return ans
ans = self.visit(queue_end, word_level_end, word_level_begin)
if ans:
return ans
return 0
def visit(self, queue, word_level, other_word_level):
cur = queue.pop(0)
for i in range(self.length):
for word in self.dic[cur[:i] + "*" + cur[i + 1:]]:
if word not in word_level:
word_level[word] = word_level[cur] + 1
queue.append(word)
if word in other_word_level:
return word_level[word] + other_word_level[word] + 1
return None
困难
给定两个单词(beginWord 和 endWord)和一个字典 wordList,找出所有从 beginWord 到 endWord 的最短转换序列。转换需遵循如下规则:
每次转换只能改变一个字母。
转换后得到的单词必须是字典中的单词。
说明:
如果不存在这样的转换序列,返回一个空列表。
所有单词具有相同的长度。
所有单词只由小写字母组成。
字典中不存在重复的单词。
你可以假设 beginWord 和 endWord 是非空的,且二者不相同。
示例 1:
输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
输出:
[
["hit","hot","dot","dog","cog"],
["hit","hot","lot","log","cog"]
]
示例 2:
输入:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
输出: []
解释: endWord "cog" 不在字典中,所以不存在符合要求的转换序列。
这一题在最短序列的基础上,输出这些序列
很容易想到深度优先遍历
借助第127题第一种方式得到了不同节点所在层数记录,来实现DFS的层数变化
from collections import defaultdict
from typing import List
class Solution(object):
def __init__(self):
self.res = []
self.dic = defaultdict(set) # 保存同种模式下的单词信息
self.length = 0 # 每个单词长度
self.word_level = {} # 记录各单词节点层数
def findLadders(self, beginWord: str, endWord: str, wordList: List[str]) -> List[List[str]]:
if endWord not in wordList:
return []
self.length = len(beginWord)
for word in wordList:
for i in range(self.length):
self.dic[word[:i] + "*" + word[i + 1:]].add(word)
self.bfs(beginWord)
self.dfs(beginWord, endWord, [beginWord])
return self.res
# 记录单词层数
def bfs(self, beginWord):
self.word_level[beginWord] = 0
queue = [beginWord]
while queue:
cur = queue.pop(0)
for word in self.get_next_words(cur):
if word not in self.word_level:
self.word_level[word] = self.word_level[cur] + 1
queue.append(word)
# 回溯查找
def dfs(self, cur, target, temp):
if cur == target:
self.res.append(temp[:])
return
for word in self.get_next_words(cur):
# 如果当前单词cur的下一层不包含该邻居节点word,直接跳过
# 由于没有设置visit集合来标记是否已经遍历过,word可能为cur上一层节点
if self.word_level[cur] + 1 != self.word_level[word]:
continue
temp.append(word)
self.dfs(word, target, temp)
temp.pop()
# 获取邻居节点
def get_next_words(self, cur):
words = []
for i in range(self.length):
for word in self.dic[cur[:i] + "*" + cur[i + 1:]]:
words.append(word)
return words
if __name__ == '__main__':
beginWord = "hit"
endWord = "cog"
wordList = ["hot", "dot", "dog", "lot", "log", "cog"]
print(Solution().findLadders(beginWord, endWord, wordList))
作者:23hdsdh
链接:https://www.pythonheidong.com/blog/article/468440/e7b1b593492086930fca/
来源:python黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 python黑洞网 All Rights Reserved 版权所有,并保留所有权利。 京ICP备18063182号-1
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!