Given two words (beginWord and endWord), and a dictionary’s word list, find the length of shortest transformation sequence from beginWord to endWord, such that:
Only one letter can be changed at a time.
Each transformed word must exist in the word list. Note that beginWord is not a transformed word.
Note:
Return 0 if there is no such transformation sequence.
All words have the same length.
All words contain only lowercase alphabetic characters.
You may assume no duplicates in the word list.
You may assume beginWord and endWord are non-empty and are not the same.
Example 1:
Input:
beginWord = “hit”,
endWord = “cog”,
wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]
Output: 5
Explanation: As one shortest transformation is “hit” -> “hot” -> “dot” -> “dog” -> “cog”,
return its length 5.
Example 2:
Input:
beginWord = “hit”
endWord = “cog”
wordList = [“hot”,“dot”,“dog”,“lot”,“log”]
Output: 0
Explanation: The endWord “cog” is not in wordList, therefore no possible transformation.
给出一个beginWord,一个endWord,和一个wordList
让beginWord中每次变动一个字母后,得是wordList里面的某一个单词,而且单词不能重复使用,问经过几次变动可以变成endWord,返回step数,如果不能达到要求,返回0
思路:
单向BFS 和 双向BFS (参考某大牛的方法)
单向BFS思路:
比如Example1
beginWord = “hit”, 是开始的queue
取出hit,按位从’a’变到’z’,看变动后是否在wordList中,如果在,加入queue作下一level的展开
比如第一位’h’,从’a’到’z’可以变成’ait’, ‘bit’, …, ‘zit’, 发现都不在wordList里
再变第2位’i’,从’a’到’z’可以变成’hat’, ‘hbt’, …, ‘hzt’,其中’hot’在wordList里,加入queue,同时为了避免重复,从wordList中删除’hot’
同理第3位,发现没有
所以下一level的展开对象是’hot’
如此一步一步展开,直到得到endWord,如果所有位都处理完仍没有,返回0
time complexity: O(n * 26len)
双向BFS:
交替展开beginWord和endWord,直到在中间某一点相遇
beginWord的展开存在q1
endWord的展开存在q2
如果中间q1展开的单词存在q2中,则返回step+1
q1, q2中的某一个中间结果都不在wordList中则返回0
trick:
因为是交替展开,每次选q1和q2中较小size的展开,可以节省时间复杂度
注意
List和HashSet的contains方法
因为中间要判断单词是否存在于wordList中,要用到contains方法,
而wordList是List类型,List的contains方法底层实现是for loop,O(n)复杂度
而HashSet的contains底层实现是直接利用key,O(1)复杂度
本题如果直接用List,时间是967ms, 而转成HashSet后是13ms
双向BFS:
//13ms
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
HashSet<String> dict = new HashSet<String>(wordList);
if (!dict.contains(endWord)) {
return 0;
}
int len = beginWord.length();
HashSet<String> q1 = new HashSet<String>() {{add(beginWord);}};
HashSet<String> q2 = new HashSet<String>() {{add(endWord);}};
int step = 0;
while (!q1.isEmpty() && !q2.isEmpty()) {
step ++;
if (q1.size() > q2.size()) { //swap q1 and q2..
HashSet<String> tmp = q2;
q2 = q1;
q1 = tmp;
}
HashSet<String> queue = new HashSet<>();
for (String word : q1) {
char[] cWord = word.toCharArray();
for (int i = 0; i < len; i++) {
char before = cWord[i];
for (char c = 'a'; c <= 'z'; c++) {
cWord[i] = c;
String changed = String.valueOf(cWord);
if (q2.contains(changed)) {
return step + 1;
}
if (dict.contains(changed)) {
queue.add(changed);
dict.remove(changed);
}
}
cWord[i] = before;
}
}
q1 = queue;
}
return 0;
}