LeetCode 126 - 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.

Solution 1:

public List<List<String>> findLadders(String start, String end, Set<String> dict) {
    List<List<String>> result = new ArrayList<>();
    if(start == null || end == null || start.length()!=end.length()) return result;
    
    int minLen = Integer.MAX_VALUE; // shortest transform sequence length
    Map<String, Set<String>> visited = new HashMap<>(); // <nextword, previous_words>
    Map<String, Integer> level = new HashMap<>(); // <nextword, level>
    Queue<String> queue = new LinkedList<>(); // BFS next word queue
    
    visited.put(start, new HashSet<>()); // start word has no previous words
    level.put(start, 1);
    queue.offer(start);
    
    while (!queue.isEmpty()) {
        String word = queue.poll();
        int preLevel = level.get(word);
        if (preLevel >= minLen) continue;
        char[] chars = word.toCharArray();
        for (int i = 0; i < word.length(); i++) {
            char old = chars[i];
            for (char letter = 'a'; letter <= 'z'; letter++) {
                if(letter == old) continue;
                chars[i] = letter;
                String nextWord = new String(chars);
                if(!dict.contains(nextWord)) continue;
                
                // if level doesn't contain next word
                if(!level.containsKey(nextWord)) {
                    Set<String> preWords = new HashSet<>();
                    preWords.add(word);
                    visited.put(nextWord, preWords);
                    level.put(nextWord, preLevel + 1);
                    queue.add(nextWord);
                    
                // if level contains next word and near the start
                } else if(level.get(nextWord) > preLevel) { 
                    visited.get(nextWord).add(word);
                }
                
                if (nextWord.equals(end)) {
                    minLen = preLevel + 1;
                }
            }
            chars[i] = old;
        }
    }
    
    buildPaths(end, visited, new LinkedList<>(), result);
    return result;
}

private void buildPaths(String end, Map<String, Set<String>> visited, 
                        List<String> path, List<List<String>> paths) {  
    path.add(0, end);
    Set<String> preWords = visited.get(end);
    if(preWords == null) return; // can't transform from start to end
    if (preWords.size() == 0) { // reached start word
        paths.add(new ArrayList<String>(path));
    } else {  
        for (String pre : preWords) {
            buildPaths(pre, visited, path, paths);  
        }  
    }  
    path.remove(0);  
}

 

 

Solution 2:

private class Node {
	String word;
	int depth;
	public Node(String w, int d) {
		word = w;
		depth = d;
	}
}

public List<List<String>> findLadders(String start, String end, Set<String> dict) {
	List<List<String>> paths = new ArrayList<List<String>>();
	if (start == null || end == null || start.length() == 0) return paths;
	// maintain a hashmap for visited words
	Map<String, List<Node>> visited = new HashMap<String, List<Node>>();
	// BFS to find the minimum sequence length
	getMinLength(start, end, dict, visited);
	// DFS to back trace paths from end to start
	buildPaths(end, start, visited, new LinkedList<String>(), paths);
	return paths;
}

/**
 * Use BFS to find the minimum transformation sequences length from start to
 * end. Also store parent nodes from previous level for each visited valid word.
 */
private void getMinLength(String start, String end, Set<String> dict,
		Map<String, List<Node>> visited) {
	// maintain a queue for words, depth and previous word during BSF
	Queue<Node> queue = new LinkedList<Node>();
	queue.add(new Node(start, 1));
	dict.add(end);
	int lastLevel = 0;
	while (!queue.isEmpty()) {
		Node node = queue.poll();
		if (lastLevel > 0 && node.depth >= lastLevel) break;
		// find transformable words in next level
		for (int i = 0; i < node.word.length(); ++i) {
			StringBuilder sb = new StringBuilder(node.word);
			char original = sb.charAt(i);
			for (char c = 'a'; c <= 'z'; ++c) {
				if (c == original) continue;
				sb.setCharAt(i, c);
				String s = sb.toString();
				// if hits end, mark the current depth as the last level
				if (s.equals(end) && lastLevel == 0) {
					lastLevel = node.depth + 1;
				}
				if (dict.contains(s) && !s.equals(start)) {
					List<Node> pres = visited.get(s);
					if (pres == null) {
						// enqueue unvisited word
						queue.add(new Node(s, node.depth + 1));
						pres = new ArrayList<Node>();
						visited.put(s, pres);
						pres.add(node);
					} else if (pres.get(0).depth == node.depth) {
						// parent nodes should be in the same level - to
						// avoid circle in graph
						pres.add(node);
					}
				}
			}
		}
	}
}

/* Use DFS to back trace all paths from end to start. */
private void buildPaths(String s, String start,
		Map<String, List<Node>> visited, List<String> path,
		List<List<String>> paths) {
	path.add(0, s);
	if (s.equals(start)) {
		List<String> p = new ArrayList<String>(path);
		paths.add(p);
	} else {
		List<Node> pres = visited.get(s);
		if (pres != null) {
			for (Node pre : pres) {
				buildPaths(pre.word, start, visited, path, paths);
			}
		}
	}
	path.remove(0);
}

Reference:

http://n00tc0d3r.blogspot.com/2013/07/word-ladder-ii.html

http://decomplexify.blogspot.com/2014/05/word-ladder-ii.html

你可能感兴趣的:(LeetCode 126 - Word Ladder II)