题目链接

LeetCode

题目描述

字典 wordList 中从单词 beginWordendWord转换序列 是一个按下述规格形成的序列 beginWord -> s1 -> s2 -> ... -> sk

  • 每一对相邻的单词只差一个字母。
  • 对于 1 <= i <= k 时,每个 si 都在 wordList 中。注意, beginWord不需要在 wordList 中。
  • sk == endWord

给你两个单词 beginWordendWord 和一个字典 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
  • beginWordendWordwordList[i] 由小写英文字母组成
  • beginWord != endWord
  • wordList 中的所有字符串 互不相同

    解题思路

    方法一:BFS

  • 「转换」意即:两个单词对应位置只有一个字符不同,例如 “hit” 与 “hot”,这种转换是可以逆向的,因此,根据题目给出的单词列表,可以构建出一个无向(无权)图;

127. 单词接龙*** - 图1

  • 如果一开始就构建图,每一个单词都需要和除它以外的另外的单词进行比较,复杂度是 O(NwordLen),这里 N 是单词列表的长度;
  • 为此,我们在遍历一开始,把所有的单词列表放进一个哈希表中,然后在遍历的时候构建图,每一次得到在单词列表里可以转换的单词,复杂度是 O(26×wordLen),借助哈希表,找到邻居与 N 无关;
  • 使用 BFS 进行遍历,需要的辅助数据结构是:

    • 队列;
    • visited 集合。说明:可以直接在 wordSet (由 wordList 放进集合中得到)里做删除。但更好的做法是新开一个哈希表,遍历过的字符串放进哈希表里。这种做法具有普遍意义。绝大多数在线测评系统和应用场景都不会在意空间开销。
      1. class Solution {
      2. // 存放所有未被用的字符串
      3. Set<String> wordSet;
      4. // 存放中间已用字符串
      5. Queue<String> q;
      6. public int ladderLength(String beginWord, String endWord, List<String> wordList) {
      7. // 第 1 步:先将 wordList 放到哈希表里,便于判断某个单词是否在 wordList 里
      8. wordSet = new HashSet<String>();
      9. for(String s : wordList){
      10. wordSet.add(s);
      11. }
      12. // 如果不包含 endWord
      13. if(!wordSet.contains(endWord)){
      14. return 0;
      15. }
      16. // 删除开始字符串
      17. wordSet.remove(beginWord);
      18. // 将开始字符串加入队列
      19. q = new LinkedList<String>();
      20. q.offer(beginWord);
      21. // 开始广度优先遍历,包含起点,因此初始化的时候步数为 1
      22. int step = 1;
      23. while(!q.isEmpty()){
      24. // 遍历当前所有步数为 step 的单词
      25. int curWordSize = q.size();
      26. for(int i = 0; i < curWordSize; ++i){
      27. // 依次遍历当前队列中的单词
      28. String curWord = q.poll();
      29. // 如果 currentWord 能够修改 1 个字符与 endWord 相同,则返回 step + 1
      30. if(bfs(curWord, endWord)){
      31. return step + 1;
      32. }
      33. }
      34. // 否则加一步,遍历下一个step
      35. ++step;
      36. }
      37. return 0;
      38. }
      39. private boolean bfs(String curWord, String endWord){
      40. char[] charArr = curWord.toCharArray();
      41. // 尝试对 currentWord 修改每一个字符,看看是不是能与 endWord 匹配
      42. for(int i = 0; i < endWord.length(); ++i){
      43. // 先保存当前位置字符,然后恢复
      44. char originChar = charArr[i];
      45. for(char j = 'a'; j <= 'z'; ++j){
      46. // 如果当前位置相同,跳过,没修改
      47. if(j == charArr[i]){
      48. continue;
      49. }
      50. // 修改字符
      51. charArr[i] = j;
      52. String nextWord = String.valueOf(charArr);
      53. // 如果 wordSet 中存在改变一个字符的字符串
      54. if(wordSet.contains(nextWord)){
      55. // 如果是 endWord,直接返回 true
      56. if(nextWord.equals(endWord)){
      57. return true;
      58. }
      59. // 将下个字符串存入队列
      60. q.offer(nextWord);
      61. // 将下个字符串从 wordSet 中删除
      62. wordSet.remove(nextWord);
      63. }
      64. }
      65. charArr[i] = originChar;
      66. }
      67. return false;
      68. }
      69. }
  • 时间复杂度 O(n)

  • 空间复杂度 O(n)