[LeetCode#127]Word Ladder

---恢复内容开始---

The problem:

Given two words (start and end), and a dictionary, find the length of shortest transformation sequence 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"]

As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.

Note:
Return 0 if there is no such transformation sequence.
All words have the same length.
All words contain only lowercase alphabetic characters.

My analysis:

This problem's solution is very elegant and tricky! It represents a new kind of problem that could be solved by using abstract graph. 
The key idea: use the graph to search. But it differs a lot from the previous problem, which has a obvious link or neighbor list of a node. We need to extract this information from a very tricky way. 
The abstract graph:
1. represent each String in the dictionary as a node in the graph.
2. iff two nodes differ in only one character, there is a edge between those two nodes in the graph.

The shortest problem in the grpah:(unweighted graph)
The breadth first search could layered the nodes in the graph. 
The path from source node to another node in the breadth first traversal is the shortest path from source node to the node. The distance is the layer the node in. 

It seems that there is no linkage domain, how could we traversal?
Since we have no link to follow, how about we try all possible pathes according to the requirement!
The requirement: Only one letter can be changed at a time. It is not hard to simulate. 
for (int i = 0; i < cur.length(); i++) {
    char[] cur_array = cur.toCharArray();
    for (char c = 'a'; c <= 'z'; c++) {
        cur_array[i] = c;
        String temp = new String(cur_array);
    ...
}
However, the while loop could result in all possbile branches(which only differ a character with the current node). We could only search along the edge that existed in the graph, we need to rule out some unreasonable branches.
if (dict.contains(temp) && !visited.contains(temp)) {
    queue.offer(temp);
    visited.add(temp);
    next_num++;
}

What a beautful way!!!(fake traversal and rule out!)

Note:
the above rountine is for continue search, which means we have not reach out the "end(target)" String. However, if we reach the target, we could directly return the distance, cause the target need not to be in the dict. 
for (int i = 0; i < cur.length(); i++) {
    char[] cur_array = cur.toCharArray();
    for (char c = 'a'; c <= 'z'; c++) {
        cur_array[i] = c;
        String temp = new String(cur_array); 
        if (temp.equals(end))
            return level + 1;
    ...
    }
}

The time complexity analysis:
while (!queue.isEmpty()) {
    ...
    for (int i = 0; i < cur.length(); i++) {
    ...
        for (char c = 'a'; c <= 'z'; c++) {
        ...
            if (dict.contains(temp) && !visited.contains(temp)) {
                queue.offer(temp);
                visited.add(temp);
                next_num++;
            }
        }
    }
}
The time complexity for the big for loop: Length * 26
Since we use "dict.contains(temp)" to restrict searching domain. we enter the for loop at most. dict.size() times. 
thus the total complexity is 
size(dict) * length * 26

Note:
Be very careful with the function of "!visited.contains(temp)".
Unlike Binary, there could be circle in the graph, thus we must use visted set to record all nodes we have visited. Otherwise it would result in infinite loop. Cause we keep on(there might be iterleaves) enqueuing and dequeing it. 

My solution:

public class Solution {
    public int ladderLength(String start, String end, Set<String> dict) {
        
        if (start == null || end == null || dict == null || start.length() == 0 || end.length() == 0 || dict.size() == 0)
            return 0;
        
        HashSet<String> visited = new HashSet<String> ();
        Queue<String> queue= new LinkedList<String> ();
        String cur;
        
        queue.offer(start);
        int cur_num = 1;
        int next_num = 0;
        int level = 1;
        
        while (!queue.isEmpty()) {
            cur = queue.poll();
            cur_num-- ; //only intermdiate string need to be in the dict.
            for (int i = 0; i < cur.length(); i++) {
                char[] cur_array = cur.toCharArray();
                for (char c = 'a'; c <= 'z'; c++) {
                    cur_array[i] = c;
                    String temp = new String(cur_array); //must do it in this way, only change one char a time. 
                    if (temp.equals(end))
                        return level + 1;
                    if (dict.contains(temp) && !visited.contains(temp)) {//very imprtant checking condition!!!
                        queue.offer(temp);
                        visited.add(temp);
                        next_num++;
                    }
                }
            }
            
            if (cur_num == 0) {
                cur_num = next_num;
                next_num = 0;
                level++;
            }
        }
        return 0;
    }
}

---恢复内容结束---

原文地址:https://www.cnblogs.com/airwindow/p/4220300.html