[Leetcode][python]Word Ladder/Word Ladder II/单词接龙/单词接龙 II

矫情吗;* 2022-06-04 23:14 366阅读 0赞

Word Ladder

题目大意

给定一个起始字符串和一个目标字符串,现在将起始字符串按照特定的变换规则转换为目标字符串,求最少要进行多少次转换。转换规则为每次只能改变字符串中的一个字符,且每次转换后的字符串都要在给定的字符串集合中。

解题思路

参考:https://shenjie1993.gitbooks.io/leetcode-python/127%20Word%20Ladder.html
题目在2017年1月改动了,所以代码我也改动了。

因为每次变换后的字符串都要在给定的字符串组中,所以每次变化的情况都是有限的。现在把变化过程做成一个树的结构,由某一个字符串变化而来的字符串就成为该字符串的子树。参看下图的例子,我们可以得到以下几点结论:

1.我们把起始字符串当成根节点,如果在变化过程中,某一个节点是目标字符串,那么就找到了一条变化路径。

2.节点所在的高度能够反映出变化到该节点时经历了几次变化,如hot在根节点的下一层,表示变化了一次,hut和bot在更下一层,表示变化了两次。
在树上层出现过的字符串没必要在下层再次出现,因为如果该字符串是转换过程中必须经过的中间字符串,那么应该挑选上层的该字符串继续进行变化,它的转换次数少。

3.如果上一层有多个字符串可以转换为下一层同一个字符串,那么只需要找到其中一个转换关系即可,如例子中的bit和him都可以转为bim,我们只需要知道有一条关系可以走到bim就可以了,没必要找到所有的转换关系,因为这样已经可以确定进行两次转换就能变为bim。

4.基于第3和第4点,当集合中的字符串在树中出现后,就可以把它从集合中删除。这样可以防止字符串不断地循环转化。

5.至此,这个问题就变为一个深度优先遍历问题,只需要依次遍历每一层的节点,如果在该层找到了目标字符串,只要返回相应的变化次数。如果到某一层树的节点无法继续向下延伸,且没有找到目标字符。

这里写图片描述

代码

wordList转换为set

不然LTE。最终668ms

  1. class Solution(object):
  2. def ladderLength(self, beginWord, endWord, wordList):
  3. """
  4. :type beginWord: str
  5. :type endWord: str
  6. :type wordList: List[str]
  7. :rtype: int
  8. """
  9. wordSet = set(wordList)
  10. cur_level = [beginWord]
  11. next_level = []
  12. depth = 1
  13. n = len(beginWord) # 启示字符串长度
  14. while cur_level:
  15. for item in cur_level:
  16. if item == endWord:
  17. return depth
  18. for i in range(n):
  19. for c in 'abcdefghijklmnopqrstuvwxyz':
  20. word = item[:i] + c + item[i + 1:]
  21. if word in wordSet:
  22. wordSet.remove(word)
  23. next_level.append(word)
  24. # 树的下一层创建完毕
  25. depth += 1
  26. cur_level = next_level
  27. next_level = []
  28. return 0

cur_level也转换为set

按理说会更快一些,实际上并没有,但是再将next_level.append(word)变为next_level.append(str(word))后,却有提升至500ms。不甚理解。
PS:后经过试验,发现有时也会更差,看来leetcode时间果然不能信。

  1. class Solution(object):
  2. def ladderLength(self, beginWord, endWord, wordList):
  3. """
  4. :type beginWord: str
  5. :type endWord: str
  6. :type wordList: List[str]
  7. :rtype: int
  8. """
  9. wordSet = set(wordList)
  10. cur_level = set([beginWord])
  11. next_level = set()
  12. depth = 1
  13. n = len(beginWord) # 启示字符串长度
  14. while cur_level:
  15. for item in cur_level:
  16. if item == endWord:
  17. return depth
  18. for i in range(n):
  19. for c in 'abcdefghijklmnopqrstuvwxyz':
  20. word = item[:i] + c + item[i + 1:]
  21. if word in wordSet:
  22. print type(word), type(str(word))
  23. wordSet.remove(word)
  24. next_level.add(str(word))
  25. # 树的下一层创建完毕
  26. depth += 1
  27. cur_level = next_level
  28. next_level = set()
  29. return 0

Word Ladder II

题目大意

给定一个起始字符串和一个目标字符串,现在将起始字符串按照特定的变换规则转换为目标字符串,求所有转换次数最少的转换过程。转换规则为每次只能改变字符串中的一个字符,且每次转换后的字符串都要在给定的字符串集合中。

解题思路

为每次变换后的字符串都要在给定的字符串组中,所以每次变化的情况都是有限的。现在把变化过程做成一个树的结构,由某一个字符串变化而来的字符串就成为该字符串的子树。参看下图的例子,我们可以得到以下几点结论:

1.我们把起始字符串当成根节点,如果在变化过程中,某一个节点是目标字符串,那么就找到了一条变化路径。

2.节点所在的高度能够反映出变化到该节点时经历了几次变化,如hot在根节点的下一层,表示变化了一次,hut和bot在更下一层,表示变化了两次。

3.在树上层出现过的字符串没必要在下层再次出现,因为如果该字符串是转换过程中必须经过的中间字符串,那么应该挑选上层的该字符串继续进行变化,它的转换次数少。

4.如果上一层有多个字符串可以转换为下一层同一个字符串,那么只需要找到其中一个转换关系即可,如例子中的bit和him都可以转为bim,我们只需要知道有一条关系可以走到bim就可以了,没必要找到所有的转换关系,因为这样已经可以确定进行两次转换就能变为bim。

5.基于第3和第4点,当集合中的字符串在树中出现后,就可以把它从集合中删除。这样可以防止字符串不断地循环转化。

至此,这个问题就变为一个深度优先遍历问题,只需要依次遍历每一层的节点,如果在该层找到了目标字符串,只要返回相应的变化次数。如果到某一层树的节点无法继续向下延伸,且没有找到目标字符

代码

稍微有改动,应对改题目

  1. class Solution(object):
  2. def findLadders(self, beginWord, endWord, wordList):
  3. """
  4. :type beginWord: str
  5. :type endWord: str
  6. :type wordList: List[str]
  7. :rtype: List[List[str]]
  8. """
  9. def bfs(front_level, end_level, is_forward, word_set, path_dic):
  10. if len(front_level) == 0:
  11. return False
  12. if len(front_level) > len(end_level):
  13. return bfs(end_level, front_level, not is_forward, word_set, path_dic)
  14. for word in (front_level | end_level):
  15. word_set.discard(word)
  16. next_level = set()
  17. done = False
  18. while front_level:
  19. word = front_level.pop()
  20. for c in 'abcdefghijklmnopqrstuvwxyz':
  21. for i in range(len(word)):
  22. new_word = word[:i] + c + word[i + 1:]
  23. if new_word in end_level:
  24. done = True
  25. add_path(word, new_word, is_forward, path_dic)
  26. else:
  27. if new_word in word_set:
  28. next_level.add(new_word)
  29. add_path(word, new_word, is_forward, path_dic)
  30. return done or bfs(next_level, end_level, is_forward, word_set, path_dic)
  31. def add_path(word, new_word, is_forward, path_dic):
  32. if is_forward:
  33. path_dic[word] = path_dic.get(word, []) + [new_word]
  34. else:
  35. path_dic[new_word] = path_dic.get(new_word, []) + [word]
  36. def construct_path(word, end_word, path_dic, path, paths):
  37. if word == end_word:
  38. paths.append(path)
  39. return
  40. if word in path_dic:
  41. for item in path_dic[word]:
  42. construct_path(item, end_word, path_dic, path + [item], paths)
  43. front_level, end_level = {beginWord}, {endWord}
  44. path_dic = {}
  45. wordSet = set(wordList)
  46. if endWord not in wordSet:
  47. return []
  48. bfs(front_level, end_level, True, wordSet, path_dic)
  49. path, paths = [beginWord], []
  50. # print path_dic
  51. construct_path(beginWord, endWord, path_dic, path, paths)
  52. return paths

总结

第二题没好好看

发表评论

表情:
评论列表 (有 0 条评论,366人围观)

还没有评论,来说两句吧...

相关阅读

    相关 127. 单词

    127. 单词接龙 字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列 beginWord -> s1 -

    相关 DFS-单词

    题目描述 单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两

    相关 P1019 单词

    P1019 单词接龙 题目描述 单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词