[LeetCode] Word Ladder II

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

  1. Only one letter can be changed at a time
  2. Each intermediate word must exist in the dictionary

For example,

Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]

Return

  [
    ["hit","hot","dot","dog","cog"],
    ["hit","hot","lot","log","cog"]
  ]

Note:

  • All words have the same length.

  • All words contain only lowercase alphabetic characters.
Analysis:
This is NOT an easy problem because of the time and memory requirements.

(1) From the previous Word Ladder I, we know that Breadth First Search is a better way than the DFS.

(2) The requirement of this question is to output ALL the shortest path, which means if we find one path using the BFS, then all the other shortest paths must also in this level, so the search will stop once this level ends.

(3) We need to output the exact path, so we need one way to store the paths.

(4) For each words in the BFS queue, we still need to use the previous way to generate the valid words in the dicts (from 1st to last, change every char from 'a' to 'z' ).

(5) Duplicates is permitted within a level. e.g.,
      hem -> hex -> tex -> ted
      hem->  tem -> tex -> ted,  are all valid paths.
      Draw this into a tree structure:
                        hem
                       /       \
                    hex    tem
                      |        |
                    tex     tex
                      |        |
                    ted     ted
     A solution is to erase all the words in the previous level, instead of erasing words for each word in the level.


(7) Use a map to store and retrieve the paths. map<string, vector<string> >, stores all the previous strings for current string. Retrieval of the path will need recursion.

(8) Because we have the map storing the paths, the standard queue is not needed. Because what we do now is searching each level (see the tree above), once we found the path, still need to finish that level and apply the output. So two "queue" can be used, one stores the current level words, one stores the next level words. The next level words are generated from the current level. During the generation of valid words, path can be stored at the same time. When the next level words are all generated, if the end string is included, we can output the paths, otherwise, we can erase the words in current level, and search the next level. This erasing step is helping reduce the dict, and eliminate the case that a cycle exists in the path.

Java
List<List<String>> result;
	List<String> solu;
	Map<String, List<String>> map;
	
	public void findDict2(String str, Set<String> dict, Set<String> nextSet){
		int len = str.length();
		for(int i=0;i<len;i++){
			StringBuffer sBuffer = new StringBuffer(str);
			for(char j='a';j<='z';j++){
				if(sBuffer.charAt(i)==j) continue;
				sBuffer.replace(i, i+1, ""+j);
				if(dict.contains(sBuffer.toString())){
					nextSet.add(sBuffer.toString());
					if(map.containsKey(sBuffer.toString()))
						map.get(sBuffer.toString()).add(str);
					else {
						List<String> pres = new ArrayList<>();
						pres.add(str);
						map.put(sBuffer.toString(), pres);
					}
				}
			}
		}
	}
	public List<List<String>> findLadders(String start, String end, Set<String> dict) {
		map = new HashMap<>();
		result = new ArrayList<>();
		solu = new ArrayList<>();
		dict.add(start);
		dict.add(end);
		Set<String> curSet = new HashSet<>();
		curSet.add(start);
		Set<String> nextSet = new HashSet<>();
		solu.add(end);
		
		while(dict.size()>0){
			Iterator<String> iterator = curSet.iterator();
			while(iterator.hasNext()){
				String str = iterator.next();
				dict.remove(str);
			}
			Iterator<String> iterator2 = curSet.iterator();
			while(iterator2.hasNext()){
				findDict2(iterator2.next(), dict, nextSet);
			}
			if(nextSet.isEmpty()) return result;
			if(nextSet.contains(end)){
				output(start, end);
				return result;
			}
			curSet.clear();
			curSet.addAll(nextSet);
			nextSet.clear();
		}
		return result;
    }
	public void output(String start, String end){
		if(end.equals(start)){
			Collections.reverse(solu);
			result.add(new ArrayList<>(solu));
			Collections.reverse(solu);
		}else {
			for(int i=0;i<map.get(end).size();i++){
				solu.add(map.get(end).get(i));
				output(start, map.get(end).get(i));
				solu.remove(solu.size()-1);
			}
		}
	}

c++
public:
    unordered_map<string,vector<string> > mp; // result map
    vector<vector<string> > res;
    vector<string> path;
     
    void findDict2(string str, unordered_set<string> &dict,unordered_set<string> &next_lev){
        int sz = str.size();
        string s = str;
        for (int i=0;i<sz;i++){
            s = str;
            for (char j = 'a'; j<='z'; j++){
                s[i]=j;
                if (dict.find(s)!=dict.end()){
                    next_lev.insert(s);
                    mp[s].push_back(str);
                }
            }
        }
    }
     
    void output(string &start,string last){
        if (last==start){
            reverse(path.begin(),path.end());
            res.push_back(path);
            reverse(path.begin(),path.end());
        }else{
            for (int i=0;i<mp[last].size();i++){
                path.push_back(mp[last][i]);
                output(start,mp[last][i]);
                path.pop_back();
            }
        }
    }
     
    vector<vector<string>> findLadders(string start, string end, unordered_set<string> &dict) {
        mp.clear();
        res.clear();
        path.clear();
         
        dict.insert(start);
        dict.insert(end);
         
        unordered_set<string> cur_lev;
        cur_lev.insert(start);
        unordered_set<string> next_lev;
        path.push_back(end);
         
         
        while (true){
            for (auto it = cur_lev.begin();it!=cur_lev.end();it++){dict.erase(*it);} //delete previous level words
             
            for (auto it = cur_lev.begin();it!=cur_lev.end();it++){  //find current level words
                findDict2(*it,dict,next_lev);
            }
             
            if (next_lev.empty()){return res;}
             
            if (next_lev.find(end)!=dict.end()){ //if find end string
                output(start,end);
                return res;
            }
             
            cur_lev.clear();
            cur_lev = next_lev;
            next_lev.clear();
        }
        return res;    
    }



你可能感兴趣的:(LeetCode,Queue,map,bfs)