给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:
1,每次转换只能改变一个字母。
2,转换过程中的中间单词必须是字典中的单词。
说明:
-
如果不存在这样的转换序列,返回 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" 不在字典中,所以无法进行转换。
答案:
1public int ladderLength1(String beginWord, String endWord, List<String> wordList) {
2 Set<String> dict = new HashSet<>(wordList);
3 Set<String> vis = new HashSet<>();
4 Queue<String> q = new LinkedList<>();
5 q.add(beginWord);
6 int len = 1;
7 while (!q.isEmpty()) {
8 for (int i = q.size(); i > 0; i--) {
9 String w = q.poll();
10 if (w.equals(endWord))
11 return len;
12 for (int j = 0; j < w.length(); j++) {
13 char[] ch = w.toCharArray();
14 for (char c = 'a'; c <= 'z'; c++) {
15 if (c == w.charAt(j))
16 continue;
17 ch[j] = c;
18 String nb = String.valueOf(ch);
19 if (dict.contains(nb) && vis.add(nb))
20 q.add(nb);
21 }
22 }
23 }
24 len++;
25 }
26 return 0;
27}
解析:
如果熟悉bfs的同学,估计对这些代码理解起来会更容易一些。disc是字典集合,vis主要是判断集合disc中的元素是否被访问过。其中第17行会改变单词中的一个字符,然后在19行在判断这个单词是否是在字典中,并且是否被访问过,如果字典中有并且没有被访问过,那么就加入到队列q中。注意这里的第8行是不能去掉的,因为第20行加入q中的都是只改变一个字符的单词,只相当于转换了一次。下面来看一种递归的解法
1public int ladderLength(String beginWord, String endWord, List<String> wordList) {
2 if (wordList == null || wordList.size() == 0)
3 return 0;
4 HashSet<String> start = new HashSet<>();
5 HashSet<String> dic = new HashSet<>(wordList);
6 start.add(beginWord);
7 if (!dic.contains(endWord))
8 return 0;
9 return bfs(start, endWord, dic, 2);
10
11}
12
13public int bfs(HashSet<String> start, String end, HashSet<String> dic, int len) {
14 if (start.size() == 0)
15 return 0;
16 dic.removeAll(start);
17 HashSet<String> next = new HashSet<>();
18 for (String s : start) {
19 char[] arr = s.toCharArray();
20 for (int i = 0; i < arr.length; i++) {
21 char tmp = arr[i];
22 for (char c = 'a'; c <= 'z'; c++) {
23 if (tmp == c)
24 continue;
25 arr[i] = c;
26 String nstr = new String(arr);
27 if (dic.contains(nstr)) {
28 if (end.equals(nstr))
29 return len;
30 else
31 next.add(nstr);
32 }
33 }
34 arr[i] = tmp;
35 }
36 }
37 return bfs(next, end, dic, len + 1);
38}
其实原理都差不多,每递归一次,len就加1。第14行如果条件成立,说明查找的时候出现了断裂,直接返回0即可。我们先看一下13行的start,再看37行的递归,发现start就是递归的时候传递的next,而next是在17行初始化的,只有执行到31行才会有值。其实第17行到36行代码和上面那种方法差不多。第16行是去掉重复的。