126-Word Ladder II

kevin聰發表於2018-04-24

Description

Given two words (beginWord and endWord), and a dictionary’s word list, find all shortest transformation sequence(s) from beginWord to endWord, such that:

  1. Only one letter can be changed at a time
  2. Each transformed word must exist in the word list. Note that beginWord is not a transformed word.

Note:

  • Return an empty list 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:
[
  ["hit","hot","dot","dog","cog"],
  ["hit","hot","lot","log","cog"]
]

Example 2:

Input:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]

Output: []

Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.

問題描述

給定兩個單詞, beginWord和endWord, 還有一個單詞列表wordList, 返回所有的使beginWord轉換為endWord的最短的轉換路徑。每次轉換規則如下

  1. 一次只能改變一個字母
  2. 每次轉換的單詞必須在wordList中, 注意beginWord不在wordList中

問題分析

最短的轉換, 想到了BFS。為了提高效率, 使用雙向BFS。
即使用雙向BFS構造圖, 然後通過dfs遍歷圖, 找出能夠到達endWord的路徑


解法

class Solution {
    public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
        //雙向BFS中的set1和set2
        Set<String> words1 = new HashSet();
        Set<String> words2 = new HashSet();
        //將字典轉換為set, 提高效率
        Set<String> dict = new HashSet(wordList);
        if(!dict.contains(endWord)) return new ArrayList<List<String>>();
        words1.add(beginWord);
        words2.add(endWord);
        //圖, 存放字串可以轉換的字串
        Map<String, List<String>> next = new HashMap();
        //構造圖
        helper(words1, words2, dict, next, false);

        List<List<String>> res = new ArrayList();
        List<String> path = new ArrayList(Arrays.asList(beginWord));
        //通過dfs構造路徑
        getPath(beginWord, endWord, next, path, res);

        return res;
    }
    //雙向BFS構造圖
    private boolean helper(Set<String> words1, Set<String> words2, Set<String> dict, Map<String, List<String>> next, boolean words1IsBegin){
        if(words1.isEmpty())  return false;
        if(words1.size() > words2.size())   return helper(words2, words1, dict, next, !words1IsBegin);
        //注意這裡, 先刪去words1和words2中的字串, 以免重複遍歷
        dict.removeAll(words1);
        dict.removeAll(words2);
        //下層遞迴的beginWords     
        Set<String> words3 = new HashSet();
        boolean reach = false;

        for(String str : words1){
            char[] cArr = str.toCharArray();
            for(int i = 0;i < str.length();i++){
                //注意這裡的處理, 為了提高效率, 預先儲存的i處的字元
                char loc = cArr[i];
                for(char c = 'a'; c <= 'z';c++){
                    if(loc == c)    continue;
                    cArr[i] = c;
                    String temp = new String(cArr);
                    //注意這裡, 因為使用的是雙向BFS, 構建路徑需要注意方向
                    String key = words1IsBegin ? temp : str;
                    String val = words1IsBegin ? str : temp;

                    List<String> list = next.containsKey(key) ? next.get(key) : new ArrayList();
                    if(words2.contains(temp)){
                        reach = true;
                        list.add(val);
                        next.put(key, list);
                    }else if(!reach && dict.contains(temp)){
                        words3.add(temp);
                        list.add(val);
                        next.put(key, list);
                    }
                    cArr[i] = loc;
                }
            }
        }
        //注意!words1IsBegin , 轉換了方向
        return reach || helper(words2, words3, dict, next, !words1IsBegin);
    }
    //通過getPath遍歷圖
    private void getPath(String beginWord, String endWord, Map<String, List<String>> next, List<String> path, List<List<String>> paths){
        if(beginWord.equals(endWord)){
            paths.add(new ArrayList(path));
            return;
        }
        if(!next.containsKey(beginWord))    return;

        for(String word : next.get(beginWord)){
                path.add(word);
                getPath(word, endWord, next, path, paths);
                path.remove(path.size() - 1);
        }
    }
}

相關文章