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
.
Given two words, beginWord
and endWord
, and a dictionary wordList
, return all the shortest transformation sequences from beginWord
to endWord
, or an empty list if no such sequence exists. Each sequence should be returned as a list of the words [beginWord, s1, s2, ..., sk]
.
Example 1:
Copy Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
Output: [["hit","hot","dot","dog","cog"],["hit","hot","lot","log","cog"]]
Explanation: There are 2 shortest transformation sequences:
"hit" -> "hot" -> "dot" -> "dog" -> "cog"
"hit" -> "hot" -> "lot" -> "log" -> "cog"
Example 2:
Copy Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
Output: []
Explanation: The endWord "cog" is not in wordList, therefore there is no valid transformation sequence.
Constraints:
1 <= beginWord.length <= 5
endWord.length == beginWord.length
1 <= wordList.length <= 1000
wordList[i].length == beginWord.length
beginWord
, endWord
, and wordList[i]
consist of lowercase English letters.
All the words in wordList
are unique .
Solution:
https://www.jiuzhang.com/problem/word-ladder-ii/
Version 1:
从 end 到 start 做一次 BFS,并且把距离 end 的距离都保存在 distance 中。 然后在从 start 到 end 做一次 DFS,每走一步必须确保离 end 的 distance 越来越近。
这种解法是:end -> start : bfs 。start -> end : dfs
与另外一个代码中提前建立 index 不同,这里是在寻找下一个变换单词的时候,再去获得对应的单词列表。一个单词最多有 L 个字符,每个字符有 25 种不同的变化(26个字母除掉这个位置上的字母),然后 check 一下在不在 dict 里就知道是不是 next word 了。
Copy /class Solution {
public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
List<List<String>> result = new ArrayList<>();
Set<String> dict = new HashSet<String>();
// need to add this stage, otherwise it always return null
dict.add(beginWord);
for(String word : wordList)
{
dict.add(word);
}
if(!dict.contains(endWord)) return result;
Map<String, Integer> distance = new HashMap<String, Integer>();
bfs(distance, endWord, beginWord, dict);
List<String> path = new ArrayList<String>();
// first add the beginWord
path.add(beginWord);
dfs(result, path, distance, beginWord, endWord, dict);
return result;
}
public void bfs(Map<String, Integer> distance , String beginWord, String endWord, Set<String> wordList)
{
Queue<String> queue = new LinkedList<String>();
queue.offer(beginWord);
distance.put(beginWord, 0);
while(!queue.isEmpty())
{
for(int i = 0; i< queue.size(); i++)
{
String current = queue.poll();
if(current.equals(endWord))
{
return;
}
List<String> neighbors = getNeighbours(current, wordList);
for(String next: neighbors)
{
if(!distance.containsKey(next))
{
queue.offer(next);
distance.put(next, distance.get(current) + 1);
}
}
}
}
}
public void dfs(List<List<String>> result,
List<String> path,
Map<String, Integer> distance,
String currentWord,
String endWord,
Set<String> wordList)
{
//path.add(currentWord);
if(currentWord.equals(endWord))
{
result.add(new ArrayList<String>(path));
// can't add return;
}
else
{
for(String neighbor: getNeighbours(currentWord, wordList))
{
if(distance.containsKey(neighbor) && distance.containsKey(currentWord) && distance.get(currentWord) == distance.get(neighbor) + 1)
{
path.add(neighbor);
dfs(result, path, distance, neighbor, endWord, wordList);
path.remove(path.size()-1);
}
}
}
//path.remove(path.size()-1);
}
public List<String> getNeighbours(String current, Set<String> dict)
{
List<String> result = new ArrayList<String>();
for(int i = 0; i< current.length(); i++)
{
char c = current.charAt(i);
for(char j = 'a' ; j <= 'z'; j++)
{
if(c == j) continue;
char[] wordArray = current.toCharArray();
wordArray[i] = j;
String neighbor = new String(wordArray);
if(dict.contains(neighbor))
{
result.add(neighbor);
}
}
}
return result;
}
}
Version 2:
BFS from start to end寻找最短路径,DFS from end to start 找到所有路径。
思路上和Word Ladder 是比较类似的,但是因为是要求出所有路径,仅仅保存路径长度是不够的,而且这里还有更多的问题,那就是为了得到所有路径,不是每个结点访问一次就可以标记为visited了,因为有些访问过的结点也会是别的路径上的结点,所以访问的集合要进行回溯(也就是标记回未访问)。所以时间上不再是一次广度优先搜索的复杂度了,取决于结果路径的数量。同样空间上也是相当高的复杂度,因为我们要保存过程中满足的中间路径到某个数据结构 中,以便最后可以获取路径,这里我们维护一个HashMap,把一个结点前驱结点都进行保存。在LeetCode中用Java 实现上述算法 非常容易超时。为了提高算法效率,需要注意一下两点:
1)在替换String的某一位的字符时,先转换成char数组再操作;
2)如果按照正常的方法从start找end,然后根据这个来构造路径,代价会比较高,因为保存前驱结点容易,而保存后驱结点则比较困难。
所以我们在广度优先搜索时反过来先从end找start,最后再根据生成的前驱结点映射从start往end构造路径,这样算法效率会有明显提高。
Copy public List<List<String>> findLadders(String start, String end, Set<String> dict) {
//store the node and its neighbor
HashMap<String,List<String>> graph=new HashMap<String,List<String>>();
//store the distance with end
HashMap<String,Integer> distance=new HashMap<String,Integer>();
dict.add(start);
dict.add(end);
bfs(start,end,dict,graph,distance);
List<List<String>> result=new ArrayList<>();
List<String> path=new ArrayList<String>();
dfs(result,path,start,end,graph,distance);
return result;
}
public List<String> neighbor(String str,Set<String> dict){
List<String> neighbor=new ArrayList<String>();
for(int i=0;i<str.length();i++){
char ch=str.charAt(i);
for(char j='a';j<='z';j++){
if(ch==j) continue;
//这三句话生成新的string
char[] string=str.toCharArray();
string[i]=j;
String newstr=new String(string);
if(dict.contains(newstr)){
neighbor.add(newstr);
}
}
}
return neighbor;
}
//from start to end
public void bfs(String start,
String end,
Set<String> dict,
HashMap<String,List<String>> graph,
HashMap<String,Integer> distance){
Queue<String> queue=new LinkedList<String>();
queue.add(start);
distance.put(start,0);
//initial graph
for(String str: dict){
graph.put(str, new ArrayList<String>());
}
while(!queue.isEmpty()){
String current=queue.poll();
for(String element :neighbor(current,dict)){
graph.get(current).add(element);
if(!distance.containsKey(element)){
distance.put(element,distance.get(current)+1);
queue.offer(element);
}
}
}
}
//from end to start
public void dfs(List<List<String>> result,
List<String> path,
String start,
String current,
HashMap<String,List<String>> graph,
HashMap<String,Integer> distance){
path.add(current);
if(current.equals(start)){
Collections.reverse(path);
result.add(new ArrayList<String>(path));
Collections.reverse(path);
}else{
for(String next :graph.get(current)){
if(distance.containsKey(next) && distance.get(current)==distance.get(next)+1){
dfs(result,path,start,next,graph,distance);
}
}
}
path.remove(path.size()-1);
}