[Leetcode 126] Word Ladder II (hard)

Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that:

  • Only one letter can be changed at a time
  • Each intermediate word must exist in the dictionary
Example
Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]
Return
  [
    ["hit","hot","dot","dog","cog"],
    ["hit","hot","lot","log","cog"]
  ]
思路 2. (mapping 只存每个节点其下一层的节点!!!!)
  1. HashMap> mapping: 存每个节点其下一层的节点
  2. HashMap distance: 存每个节点所在的层数(从0开始)

需要2步来处理

  1. BFS得到mapping (需要distance的帮助)
  2. 在mapping的基础上用DFS找到所有路径
class Solution {
    public List> findLadders(String beginWord, String endWord, List wordList) {
        List> result = new ArrayList<> ();
        if (wordList == null || wordList.size () == 0 || !wordList.contains (endWord)) {
            return result;
        }
        
        // all neighbors of a word
        Map> mapOfWordAndNeighbors = new HashMap<> ();
        
        // distance from the beginWord
        Map distance = new HashMap<> ();
        distance.put (beginWord, 0);
        
        // convert List into Set: to avoid timeout limited
        Set wordSet = new HashSet<> (wordList);
        
        // 1. BFS to get words neighbors (only get its next layer's)
        getWordNeighborsMap (wordSet, beginWord, endWord, mapOfWordAndNeighbors, distance);
        
        if (mapOfWordAndNeighbors == null) {
            return result;
        }
        
        // 2. DFS to get all paths
        List combinationEntry = new ArrayList<> ();
        combinationEntry.add (beginWord);
        getAllPaths (beginWord, endWord, mapOfWordAndNeighbors, combinationEntry, result);
        
        return result;
    }
    
    // using BFS to get word neighbor 
    // WARNING!!!!!: Don't use usedWords (HashSet<>), it's tough to handle the endWorld which has been visited
    // USE DISTANCE MAP, if a neighbor candidate is not in the distance map, or it's in the distance map but it is in the same layer (has the same distance)
    // then this neighbor a valid one, can put to the neighbor map 
    private void getWordNeighborsMap (Set wordSet,
                                      String beginWord,
                                      String endWord,
                                      Map> mapOfWordAndNeighbors,
                                      Map distance) 
    {
        Queue currentLayer = new LinkedList<> ();
        Queue nextLayer = new LinkedList<> ();
        currentLayer.offer (beginWord);
                
        while (!currentLayer.isEmpty ()) {
            String current = currentLayer.poll ();
            int currentDistance = distance.get (current);
            
            for (int i = 0; i < current.length (); i++) {
                for (char ch = 'a'; ch <= 'z'; ch++) {
                    if (current.charAt (i) == ch)
                        continue;
                    
                    char[] tempChar = current.toCharArray ();
                    tempChar [i] = ch;
                    String tempNeighbor = String.valueOf (tempChar);
                    
                    if (wordSet.contains (tempNeighbor) && (!distance.containsKey (tempNeighbor) || distance.getOrDefault (tempNeighbor, -1)  == currentDistance + 1)) {
                        nextLayer.offer (tempNeighbor);

                        // 1) update the neighbor map
                        Set neighbors = mapOfWordAndNeighbors.getOrDefault (current, new HashSet<> ());
                        neighbors.add (tempNeighbor);
                        mapOfWordAndNeighbors.put (current, neighbors);
                        
                        // 2) update the distance map
                        distance.put (tempNeighbor, currentDistance + 1);
                    } 
                }
            }
            
            if (currentLayer.isEmpty ()) {
                currentDistance ++;
                currentLayer = nextLayer;
                nextLayer = new LinkedList<> ();
            }  
        }
    }
    
    // DFS: traverse the map to get all paths
    
    private void getAllPaths (String currentWord,
                              String endWord,
                              Map> mapOfWordAndNeighbors, 
                              List combinationEntry, 
                              List> result)
    {
        if (currentWord.equals (endWord)) {
            result.add (new ArrayList<> (combinationEntry));
            return;
        }
        
        for (String neighbor : mapOfWordAndNeighbors.getOrDefault (currentWord, new HashSet<> ())) {
            combinationEntry.add (neighbor);
            getAllPaths (neighbor, endWord, mapOfWordAndNeighbors, combinationEntry, result);
            combinationEntry.remove (combinationEntry.size () - 1);
        }
    }
}
思路 1. (mapping 存每个节点所有邻接点)

可以将这个word看做一个graph,找到其所有最短路径,需要2个数据结构。

  1. HashMap> mapping: 存每个节点的所有邻接点
  2. HashMap distance: 存每个节点所在的层数(从0开始)

需要2个函数来处理
getGraphicInfo: 填充mapping和distance (BFS算法)

public void getGraphicInfo(HashMap> mapping,
                               HashMap distance,
                               String start,
                               Set dict {…}
  1. 用queue来存储graphic的节点
  2. queue中每个节点都取其neighbors
    a. 将该点的所有neighbors都放入mapping中对应的位置。
    b. 判断该neighbor是否已在distance中,如不在则说明并未访问过
    • 压入queue
    • 在distance中加入该neighbor的distance == curStr的distance + 1

getLadder:递归找到所有path(DFS算法)

public void getLadder(HashMap> mapping,
                          HashMap distance,
                          String start, String curStr,
                          Set dict,
                          List> result,
                          List path) {... }

从后往前找path

  1. 每次进入该函数,path.add(curStr)
  2. 判断是否当前curStr节点==start,相等则代表找到了目标,将path加入到result中
  3. 不相等,则没有找到目标,还要继续找:
    遍历当前curStr的所有neighbors,如果是紧挨着的两个节点(curStr distance = neighbor distance + 1),那么就是最短的路劲上的节点,则将该neighbor传入getLadder()作为curStr,递归继续找
  4. 前面全部完成后,需要回朔path,每一次path都remove掉尾巴上的元素。
public class Solution {
    /**
      * @param start, a string
      * @param end, a string
      * @param dict, a set of string
      * @return a list of lists of string
      */
     /*
     public List> findLadders(String start, String end, Set dict) {
        List> result = new ArrayList<>();
        if (dict == null) {
            return result;
        }
        
        //存每个节点的相邻节点
        HashMap> mapping = new HashMap<>();
        
        //存每个节点所在的层数
        HashMap distance = new HashMap<>();
        
        dict.add(end);
        dict.add(start);
        distance.put(start, 0);
        
        //填充了mapping, distance 2个hashmap,得到了图的所有信息
        // bfs算法
        getGraphicInfo(mapping, distance, start, dict);
        //得到path。getLadder为DFS算法,所以path(每条路径)需从外部传入
        List path = new ArrayList();
        getLadder(mapping, distance, start, end, dict, result, path);
        
        return result;
    }
    
    public void getGraphicInfo(HashMap> mapping,
                               HashMap distance,
                               String start,
                               Set dict) {
        Queue queue = new LinkedList();
        queue.add(start);
        
        for (String str : dict) {
            mapping.put(str, new ArrayList());
        }
        
        while (!queue.isEmpty()) {
            String cur = queue.poll();
            
            for (String str : getNeighbors(dict, cur)) {
                //填充mapping
                mapping.get(cur).add(str);
                
                //填充distance,distance的value是str节点的前一层节点(cur节点)的层数+1
                if (!distance.containsKey(str)) {
                    distance.put(str, distance.get(cur) + 1);
                    queue.add(str); //distance中没有出现过的节点才是下一层需要加继续queue的,如果已经出现过了,则不要再放进queue了,因为是以访问过的了
                }
            }
        }
    }
    
    public void getLadder(HashMap> mapping,
                          HashMap distance,
                          String start, String curStr,
                          Set dict,
                          List> result,
                          List path) {
        //进入函数时就加一个节点
        path.add(curStr);
        if (curStr.equals(start)) {
            Collections.reverse(path);
            result.add(new ArrayList<>(path));
            Collections.reverse(path);
        } else {
            //找与当前节点相邻的节点,判断其是否是紧挨着的. 如果是则继续DFS算法继续找
            for (String str : mapping.get(curStr)) {
                if (distance.get(curStr) == distance.get(str) + 1) {
                    getLadder(mapping, distance, start, str, dict, result, path);
                }
            }
        }
        path.remove(path.size() - 1);
    }
    
    public List getNeighbors(Set dict, String cur) {
        List result = new ArrayList();
        
        for (int i = 0; i < cur.length(); i++) {
            for (char c = 'a'; c <= 'z'; c++) {
                char[] temp = cur.toCharArray();
                if (cur.charAt(i) == c) {
                    continue;
                }
                temp[i] = c;
                String newStr = String.valueOf(temp);
                if (dict.contains(newStr)) {
                    result.add(newStr);
                }
            }
        }
        return result;
    }
}

你可能感兴趣的:([Leetcode 126] Word Ladder II (hard))