package com.interview.leetcode.strings;
import java.util.*;
/**
* Created_By: stefanie
* Date: 14-11-16
* Time: 下午3:20
*
* Given two words (start and end), and a dictionary, find the length of shortest transformation sequence from start to end,
* Only one letter can be changed at a time, and each intermediate word must exist in the dictionary
* For example,
* Given: start = "hit" end = "cog" dict = ["hot","dotProduct","dog","lot","log"]
* As one shortest transformation is "hit" -> "hot" -> "dotProduct" -> "dog" -> "cog",
* return its length 5.
*
* WordLadderI is to return the min ladder number
* WordLadderII is to return the path of all the min ladder solution
*
* Solution:
* 1. both are use BSF on word graph
* 2. WordLadderII use a prev link to store the path.
*/
public class WordLadder {
static class WordLadderI {
Set<String> dict;
Set<String> visited;
Queue<String> queue;
String end;
public int length(String start, String end, Set<String> dict) {
if(start.equals(end)) return 1;
//INIT
this.dict = dict;
this.end = end;
visited = new HashSet<String>();
queue = new LinkedList<String>();
queue.offer(start);
visited.add(start);
int steps = 1;
//DO BFS
while(queue.size() > 0){
int size = queue.size();
for(int k = 0; k < size; k++){ //visit every layer
String word = queue.poll();
if(populate(word)) return steps + 1;
}
steps++;
}
return 0;
}
public boolean populate(String word){
char[] chars = word.toCharArray();
for(int i = 0; i < chars.length; i++){
char orignal = chars[i];
for(char ch = 'a'; ch <= 'z'; ch++){
if(ch == orignal) continue;
chars[i] = ch;
String next = String.valueOf(chars);
if(next.equals(end)) return true;
if(dict.contains(next) && !visited.contains(next)) {
queue.offer(next);
visited.add(next);
}
}
chars[i] = orignal;
}
return false;
}
}
static class WordLadderII {
class Node {
int depth;
String value;
List<Node> prev = new ArrayList();
Node(String value, int depth) { this.value = value; this.depth = depth; }
public boolean equals(Object o) {
if(o == null || ! (o instanceof Node)) return false;
Node other = (Node) o;
return this.value.equals(other.value);
}
}
public List<List<String>> findLadders(String start, String end, Set<String> dict) {
List<List<String>> result = new ArrayList();
if(start == null || end == null || dict.isEmpty()) return result;
int wordLength = start.length();
Node startNode = new Node(start, 0);
HashMap<String, Node> nodes = new HashMap();
nodes.put(start, startNode);
Queue<Node> queue = new LinkedList();
queue.offer(startNode);
while(! queue.isEmpty() && nodes.get(end) == null) {
int levelNodeCount = queue.size();
for(int index = 0; index < levelNodeCount; index ++) { // visit by level
Node current = queue.poll();
for(int i = 0; i < wordLength; i ++) {
StringBuilder buffer = new StringBuilder(current.value);
for(char ch = 'a'; ch <= 'z'; ch ++) {
buffer.setCharAt(i, ch);
String word = buffer.toString();
if(dict.contains(word)) {
Node wordNode = nodes.get(word);
if(wordNode == null) {
wordNode = new Node(word, current.depth + 1);
wordNode.prev.add(current);
nodes.put(word, wordNode);
queue.add(wordNode);
} else if(wordNode.depth == current.depth + 1) {
wordNode.prev.add(current);
}
}
}
}
}
}
if(nodes.get(end) != null)
findShortestPaths(nodes.get(start), nodes.get(end), new ArrayList() , result);
return result;
}
public void findShortestPaths(Node start, Node current, List<String> path, List<List<String>> result) {
path.add(0, current.value);
if (start.equals(current)) result.add(path);
else {
for (Node next : current.prev)
findShortestPaths(start, next, new ArrayList<String>(path), result);
}
}
}
}