package com.freetymekiyan.algorithms.level.medium;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayDeque;
import java.util.HashSet;
import java.util.Queue;
import java.util.Set;
/**
* Given two words (beginWord and endWord), and a dictionary's word list, find the length of shortest transformation
* sequence from beginWord to endWord, such that:
* <p>
* Only one letter can be changed at a time
* Each intermediate word must exist in the word list
* For example,
* <p>
* Given:
* beginWord = "hit"
* endWord = "cog"
* wordList = ["hot","dot","dog","lot","log"]
* As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
* return its length 5.
* <p>
* Note:
* Return 0 if there is no such transformation sequence.
* All words have the same length.
* All words contain only lowercase alphabetic characters.
* Company Tags: Amazon, LinkedIn, Snapchat, Facebook, Yelp
*/
public class WordLadder {
/**
* BFS. Level-order Traversal.
* Search from begin word to end word.
* Use an integer to track ladder length.
* Add begin word to queue.
* Remove begin word from word list to set it as visited.
* While queue is not empty:
* | Traverse each word w in this level.
* | If w == end word, return true.
* | Else from w generate all possible next word by changing one character.
* | If next word also in word list:
* | Add it to queue and remove it from word list.
* | Update ladder length after this level is finished.
* If search failed, return 0;
*/
public int ladderLength(String beginWord, String endWord, Set<String> wordList) {
if (wordList == null || wordList.size() == 0) {
return 0;
}
Queue<String> queue = new ArrayDeque<>();
queue.add(beginWord);
wordList.remove(beginWord); // Remove from set as visited.
int length = 1; // Length should be 1 at least.
while (!queue.isEmpty()) {
for (int i = queue.size(); i > 0; i--) {
String word = queue.poll();
if (word.equals(endWord)) {
return length;
}
for (int j = 0; j < word.length(); j++) {
for (char c = 'a'; c <= 'z'; c++) { // Generate neighbors.
char[] chars = word.toCharArray();
if (chars[j] == c) { // Skip same character.
continue;
}
chars[j] = c;
String nextWord = new String(chars);
if (wordList.contains(nextWord)) { // Neighboring word is in list.
queue.add(nextWord);
wordList.remove(nextWord);
}
}
}
}
length++; // Next level.
}
return 0; // Not found, return 0.
}
/**
* BFS, search from both ends.
*/
public int ladderLengthB(String beginWord, String endWord, Set<String> wordList) {
int pathLength = 2;
Set<String> start = new HashSet<>();
Set<String> end = new HashSet<>();
start.add(beginWord);
end.add(endWord);
wordList.remove(beginWord);
wordList.remove(endWord);
while (!start.isEmpty()) {
if (start.size() > end.size()) {
Set<String> temp = start;
start = end;
end = temp;
}
Set<String> next = new HashSet<>();
for (String cur : start) {
char[] strArray = cur.toCharArray();
for (int i = 0; i < strArray.length; i++) {
char old = strArray[i];
for (char c = 'a'; c <= 'z'; c++) {
strArray[i] = c;
String str = String.valueOf(strArray);
if (end.contains(str)) {
return pathLength;
}
if (wordList.contains(str)) {
next.add(str);
wordList.remove(str);
}
}
strArray[i] = old;
}
}
start = next;
pathLength++;
}
return 0;
}
@Test
public void testExamples() {
String start = "hot";
String end = "dog";
Set<String> s = new HashSet<>();
s.add("hot");
s.add("dog");
s.add("dot");
Assert.assertEquals(3, ladderLength(start, end, s));
}
}