Given two words (beginWord and endWord), and a dictionary's word list, find the length of shortest transformation sequence from beginWordto endWord, such that:
- Only one letter can be changed at a time
- Each intermediate word must exist in the word list
For example,
Given:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog"
,
return its length 5
.
Note:
- Return 0 if there is no such transformation sequence.
- All words have the same length.
- All words contain only lowercase alphabetic characters
思路:这题可以想象成一个图的结构,从根节点开始,每个position,可以有25种变换,(一共有26个字母,去掉自己),然后用dictionary查,这个位置哪一个或者哪几个在字典里面,这样,画树型结构,每个字符串的每一个节点会有25个可能的边,然后继续往下画。很自然的会想到BFS 的level order的搜索。用BFS搜索,首先generate所有的 只改变一个char的string,然后判断是否在dict里面。
注意:1. 应该想到这个题目里面有分层的思想,因为graph是可能有好几个可以变化的词。
2. 写模块化的function,这样思路更加清晰,而且不用charArray来回设置c;代码更简单简洁;
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
HashSet<String> visited = new HashSet<>();
HashSet<String> dict = new HashSet<String>(wordList);
Queue<String> queue = new LinkedList<>();
queue.offer(beginWord);
visited.add(beginWord);
int step = 0;
while(!queue.isEmpty()) {
int size = queue.size();
for(int i = 0; i < size; i++) {
String node = queue.poll();
if(node.equals(endWord)) {
return step + 1;
}
for(String neighbor: getNeighbors(node, dict)) {
if(!visited.contains(neighbor)) {
visited.add(neighbor);
queue.offer(neighbor);
}
}
}
step++;
}
return 0;
}
private List<String> getNeighbors(String node, HashSet<String> dict) {
char[] ss = node.toCharArray();
List<String> res = new ArrayList<>();
for(int i = 0; i < ss.length; i++) {
char origin = ss[i];
for(char k = 'a'; k <= 'z'; k++) {
if(k == origin) {
continue;
}
ss[i] = k;
String newstr = new String(ss);
if(dict.contains(newstr)) {
res.add(newstr);
}
ss[i] = origin;
}
}
return res;
}
}
进一步优化:双向BFS,写法很巧妙,每次扩展size最小的,startSet 和endSet,两边分别进行,一次一次的扩展最小的。BeginSet永远是最小的。这里的step==1,是因为默认已经扩展了一层;
上面step == 0,是因为刚加进去,还没开始扩展;
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
HashSet<String> dict = new HashSet<String>(wordList);
if(!dict.contains(endWord)) {
return 0;
}
HashSet<String> beginSet = new HashSet<>();
HashSet<String> endSet = new HashSet<>();
beginSet.add(beginWord);
endSet.add(endWord);
HashSet<String> visited = new HashSet<>();
int step = 1;
while(!beginSet.isEmpty() && !endSet.isEmpty()) {
if(beginSet.size() > endSet.size()) {
HashSet<String> temp = beginSet;
beginSet = endSet;
endSet = temp;
}
HashSet<String> nextset = new HashSet<>();
for(String str: beginSet) {
for(String neighbor: getNeighbors(str, dict)) {
// 注意这里是endSet包含neighbor,说明重合了,不是包含endword;
if(endSet.contains(neighbor)) {
return step + 1;
}
if(!visited.contains(neighbor)) {
visited.add(neighbor);
nextset.add(neighbor);
}
}
}
step++;
beginSet = nextset;
}
return 0;
}
private List<String> getNeighbors(String node, HashSet<String> dict) {
char[] ss = node.toCharArray();
List<String> res = new ArrayList<>();
for(int i = 0; i < ss.length; i++) {
char origin = ss[i];
for(char k = 'a'; k <= 'z'; k++) {
if(k == origin) {
continue;
}
ss[i] = k;
String newstr = new String(ss);
if(dict.contains(newstr)) {
res.add(newstr);
}
ss[i] = origin;
}
}
return res;
}
}