[LeetCode] 127. Word Ladder

A transformation sequence from word beginWord to word endWord using a dictionary wordList is a sequence of words beginWord -> s1 -> s2 -> ... -> sk such that:

  • Every adjacent pair of words differs by a single letter.
  • Every si for 1 <= i <= k is in wordList. Note that beginWord does not need to be in wordList.
  • sk == endWord

Given two words, beginWord and endWord, and a dictionary wordList, return the number of words in the shortest transformation sequence from beginWord to endWord, or 0 if no such sequence exists.

Example 1:

Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
Output: 5
Explanation: One shortest transformation sequence is "hit" -> "hot" -> "dot" -> "dog" -> cog", which is 5 words long.

Example 2:

Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
Output: 0
Explanation: The endWord "cog" is not in wordList, therefore there is no valid transformation sequence.

Constraints:

  • 1 <= beginWord.length <= 10
  • endWord.length == beginWord.length
  • 1 <= wordList.length <= 5000
  • wordList[i].length == beginWord.length
  • beginWordendWord, and wordList[i] consist of lowercase English letters.
  • beginWord != endWord
  • All the words in wordList are unique.

单词接龙。

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

每一对相邻的单词只差一个字母。
 对于 1 <= i <= k 时,每个 si 都在 wordList 中。注意, beginWord 不需要在 wordList 中。
sk == endWord
给你两个单词 beginWord 和 endWord 和一个字典 wordList ,返回 从 beginWord 到 endWord 的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0 。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/word-ladder
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

这道题的思路是BFS,不难想到。通过这道题,我们需要学会一个BFS加速的办法,双向BFS。

首先是普通的BFS。我们将 beginWord 放入队列,然后修改 beginWord 里的每一个字母,看看下一个单词是否存在于 wordList,如果存在的话,这条路就能继续走下去。我们需要返回的是一条最短路径。

时间O(n)

空间O(n)

Java实现

 1 class Solution {
 2     public int ladderLength(String beginWord, String endWord, List<String> wordList) {
 3         HashSet<String> set = new HashSet<>(wordList);
 4         // corner case
 5         if (set.size() == 0 || !set.contains(endWord)) {
 6             return 0;
 7         }
 8 
 9         // normal case
10         set.remove(beginWord);
11         Queue<String> queue = new LinkedList<>();
12         queue.offer(beginWord);
13         HashSet<String> visited = new HashSet<>();
14         int step = 1;
15         while (!queue.isEmpty()) {
16             int size = queue.size();
17             for (int i = 0; i < size; i++) {
18                 String curWord = queue.poll();
19                 if (helper(curWord, endWord, queue, visited, set)) {
20                     return step + 1;
21                 }
22             }
23             step++;
24         }
25         return 0;
26     }
27 
28     private boolean helper(String curWord, String endWord, Queue<String> queue, HashSet<String> visited, HashSet<String> set) {
29         char[] letter = curWord.toCharArray();
30         for (int i = 0; i < endWord.length(); i++) {
31             char origin = letter[i];
32             for (char j = 'a'; j <= 'z'; j++) {
33                 if (j == origin) {
34                     continue;
35                 }
36                 letter[i] = j;
37                 String nextWord = String.valueOf(letter);
38                 if (set.contains(nextWord)) {
39                     if (nextWord.equals(endWord)) {
40                         return true;
41                     }
42                     if (!visited.contains(nextWord)) {
43                         queue.offer(nextWord);
44                         visited.add(nextWord);
45                     }
46                 }
47             }
48             letter[i] = origin;
49         }
50         return false;
51     }
52 }

 

双向 BFS 的思路是我们需要对 beginWord 和 endWord 分别使用一个 hashset,并且对 beginWord 和 endWord 同时修改一个字母,看看下一个单词是否存在于 wordList。这里面还有一个技巧,假设 beginWord 和 endWord 各自衍生出了一个 nextList,我们只需要再次遍历那个更短的 nextList 以节省时间。双向 BFS 会比第一种 BFS 节省一半的时间。

时间O(n)

空间O(n)

Java实现

 1 class Solution {
 2     public int ladderLength(String beginWord, String endWord, List<String> wordList) {
 3         HashSet<String> set = new HashSet<>(wordList);
 4         // corner case
 5         if (set.size() == 0 || !set.contains(endWord)) {
 6             return 0;
 7         }
 8 
 9         // normal case
10         HashSet<String> visited = new HashSet<>();
11         HashSet<String> beginVisited = new HashSet<>();
12         beginVisited.add(beginWord);
13         HashSet<String> endVisited = new HashSet<>();
14         endVisited.add(endWord);
15 
16         int step = 1;
17         while (!beginVisited.isEmpty() && !endVisited.isEmpty()) {
18             // 优先选择小的哈希表进行扩散,考虑到的情况更少
19             if (beginVisited.size() > endVisited.size()) {
20                 HashSet<String> temp = beginVisited;
21                 beginVisited = endVisited;
22                 endVisited = temp;
23             }
24 
25             HashSet<String> nextLevelVisited = new HashSet<>();
26             for (String word : beginVisited) {
27                 if (helper(word, endVisited, visited, set, nextLevelVisited)) {
28                     return step + 1;
29                 }
30             }
31             beginVisited = nextLevelVisited;
32             step++;
33         }
34         return 0;
35     }
36 
37     private boolean helper(String word, HashSet<String> endVisited,
38             HashSet<String> visited,
39             HashSet<String> wordSet,
40             HashSet<String> nextLevelVisited) {
41         char[] letter = word.toCharArray();
42         for (int i = 0; i < word.length(); i++) {
43             char origin = letter[i];
44             for (char c = 'a'; c <= 'z'; c++) {
45                 if (origin == c) {
46                     continue;
47                 }
48                 letter[i] = c;
49                 String nextWord = String.valueOf(letter);
50                 if (wordSet.contains(nextWord)) {
51                     if (endVisited.contains(nextWord)) {
52                         return true;
53                     }
54                     if (!visited.contains(nextWord)) {
55                         nextLevelVisited.add(nextWord);
56                         visited.add(nextWord);
57                     }
58                 }
59             }
60             // 恢复,下次再用
61             letter[i] = origin;
62         }
63         return false;
64     }
65 }

 

LeetCode 题目总结

posted @ 2023-01-31 14:43  CNoodle  阅读(195)  评论(0编辑  收藏  举报