Problem:
Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that:
- Only one letter can be changed at a time
- 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.
这道题费尽周折,总是TLE,Solution1是最先提交的独立思考出来的版本。TLE的原因是维护了一个当前搜索节点的最短路径链表,而这些中间路径大部分都不会出现在最终结果中。而且,对于路径A->B->C和A->D->C,该方法会从C开始做两次搜索,如果有大量这样的冗余,TLE也就是必然的了。Solution2也是做BFS,但是它没有保存中间路径,用Map保存了每个单词的前继,最短路径找到后,回溯得到所有的最短路径。
Solution1:
public class Solution {
HashSet<String> dic;
public List<List<String>> findLadders(String start, String end, Set<String> dict) {
List<List<String>> reslList = new ArrayList<>();
if(dict==null||start==null||end==null||dict.size()==0||start.equals("")||end.equals(""))
return reslList;
List<String> ls = new ArrayList<>();
ls.add(start);
reslList.add(ls);
if(start.equals(end))
{
return reslList;
}
dic = new HashSet<>(dict);
while(!reslList.isEmpty())
{
int count = reslList.size();
boolean isFound = false;
ArrayList<String> removeWords = new ArrayList<>();
while(count-->0)
{
List<String> list = reslList.remove(0);
String str = list.get(list.size()-1);
int diffchars = 0;
for(int i=0;i<str.length();i++)
{
if(str.charAt(i)!=end.charAt(i))
diffchars++;
}
if(diffchars==1)
{
isFound = true;
list.add(end);
reslList.add(list);
}
if(isFound)
continue;
char[] chars = str.toCharArray();
for(int i=0;i<str.length();i++)
{
char temp = chars[i];
chars[i] = 'a'-1;
while(chars[i]++<'z')
{
if(chars[i]==temp)
continue;
String s = new String(chars);
if(dic.contains(s))
{
removeWords.add(s);
List<String> newList = new ArrayList<>();
newList.addAll(list);
newList.add(s);
reslList.add(newList);
}
}
chars[i] = temp;
}
}
dic.removeAll(removeWords);
if(isFound)
{
for(int i=reslList.size()-1;i>=0;i--)
{
List<String> tmp = reslList.get(i);
if(!tmp.get(tmp.size()-1).equals(end))
reslList.remove(i);
}
return reslList;
}
}
return reslList;
}
}
public List<List<String>> findLadders(String start, String end, Set<String> dict) {
List<List<String>> reslList = new ArrayList<>();
if(dict==null||start==null||end==null||dict.size()==0||start.equals("")||end.equals(""))
return reslList;
List<String> ls = new ArrayList<>();
ls.add(start);
reslList.add(ls);
if(start.equals(end))
{
return reslList;
}
dic = new HashSet<>(dict);
while(!reslList.isEmpty())
{
int count = reslList.size();
boolean isFound = false;
ArrayList<String> removeWords = new ArrayList<>();
while(count-->0)
{
List<String> list = reslList.remove(0);
String str = list.get(list.size()-1);
int diffchars = 0;
for(int i=0;i<str.length();i++)
{
if(str.charAt(i)!=end.charAt(i))
diffchars++;
}
if(diffchars==1)
{
isFound = true;
list.add(end);
reslList.add(list);
}
if(isFound)
continue;
char[] chars = str.toCharArray();
for(int i=0;i<str.length();i++)
{
char temp = chars[i];
chars[i] = 'a'-1;
while(chars[i]++<'z')
{
if(chars[i]==temp)
continue;
String s = new String(chars);
if(dic.contains(s))
{
removeWords.add(s);
List<String> newList = new ArrayList<>();
newList.addAll(list);
newList.add(s);
reslList.add(newList);
}
}
chars[i] = temp;
}
}
dic.removeAll(removeWords);
if(isFound)
{
for(int i=reslList.size()-1;i>=0;i--)
{
List<String> tmp = reslList.get(i);
if(!tmp.get(tmp.size()-1).equals(end))
reslList.remove(i);
}
return reslList;
}
}
return reslList;
}
}
Solution2:
public class Solution {
HashSet<String> dic;
HashMap<String, List<String>> preWords = new HashMap<>();
ArrayList<String> list = new ArrayList<>();
List<List<String>> allPath = new ArrayList<>();
public List<List<String>> findLadders(String start, String end, Set<String> dict) {
if(dict==null||start==null||end==null||dict.size()==0||start.equals("")||end.equals(""))
return allPath;
list.add(end);
if(start.equals(end))
{
allPath.add(list);
return allPath;
}
dic = new HashSet<>(dict);
boolean isFound = false;
LinkedList<String> curStrings = new LinkedList<>();
curStrings.add(start);
HashSet<String> visited = new HashSet<>();
while(!curStrings.isEmpty())
{
int count = curStrings.size();
while(count-->0)
{
String str = curStrings.removeFirst();
int diffchars = 0;
for(int i=0;i<str.length();i++)
{
if(str.charAt(i)!=end.charAt(i))
diffchars++;
}
if(diffchars==1)
{
isFound = true;
if(preWords.containsKey(end))
{
preWords.get(end).add(str);
}
else
{
ArrayList<String> newls = new ArrayList<>();
newls.add(str);
preWords.put(end, newls);
}
}
if(isFound)
continue;
StringBuilder strb = new StringBuilder(str);
for(int i=0;i<str.length();i++)
{
char ch = 'a'-1;
while(ch++<'z')
{
strb.setCharAt(i, ch);
if(ch==str.charAt(i))
continue;
String s = strb.toString();
if(dic.contains(s))
{
if(preWords.containsKey(s))
{
preWords.get(s).add(str);
}
else
{
ArrayList<String> newls = new ArrayList<>();
newls.add(str);
preWords.put(s, newls);
}
if(!visited.contains(s)) //这个条件判断很重要,好几次TLE都栽在这上面了
curStrings.add(s);
visited.add(s);
}
strb.setCharAt(i, str.charAt(i));
}
}
}
dic.removeAll(visited);
visited.clear();
if(isFound)
{
backTraverse(end, start);
break;
}
}
return allPath;
}
private void backTraverse(String tmp,String start) {
if(tmp.equals(start))
{
allPath.add(new ArrayList<>(list));
return;
}
if(!preWords.containsKey(tmp))
return;
for (String str : preWords.get(tmp)) {
list.add(0,str);
backTraverse(str, start);
list.remove(0);
}
}
}
HashSet<String> dic;
HashMap<String, List<String>> preWords = new HashMap<>();
ArrayList<String> list = new ArrayList<>();
List<List<String>> allPath = new ArrayList<>();
public List<List<String>> findLadders(String start, String end, Set<String> dict) {
if(dict==null||start==null||end==null||dict.size()==0||start.equals("")||end.equals(""))
return allPath;
list.add(end);
if(start.equals(end))
{
allPath.add(list);
return allPath;
}
dic = new HashSet<>(dict);
boolean isFound = false;
LinkedList<String> curStrings = new LinkedList<>();
curStrings.add(start);
HashSet<String> visited = new HashSet<>();
while(!curStrings.isEmpty())
{
int count = curStrings.size();
while(count-->0)
{
String str = curStrings.removeFirst();
int diffchars = 0;
for(int i=0;i<str.length();i++)
{
if(str.charAt(i)!=end.charAt(i))
diffchars++;
}
if(diffchars==1)
{
isFound = true;
if(preWords.containsKey(end))
{
preWords.get(end).add(str);
}
else
{
ArrayList<String> newls = new ArrayList<>();
newls.add(str);
preWords.put(end, newls);
}
}
if(isFound)
continue;
StringBuilder strb = new StringBuilder(str);
for(int i=0;i<str.length();i++)
{
char ch = 'a'-1;
while(ch++<'z')
{
strb.setCharAt(i, ch);
if(ch==str.charAt(i))
continue;
String s = strb.toString();
if(dic.contains(s))
{
if(preWords.containsKey(s))
{
preWords.get(s).add(str);
}
else
{
ArrayList<String> newls = new ArrayList<>();
newls.add(str);
preWords.put(s, newls);
}
if(!visited.contains(s)) //这个条件判断很重要,好几次TLE都栽在这上面了
curStrings.add(s);
visited.add(s);
}
strb.setCharAt(i, str.charAt(i));
}
}
}
dic.removeAll(visited);
visited.clear();
if(isFound)
{
backTraverse(end, start);
break;
}
}
return allPath;
}
private void backTraverse(String tmp,String start) {
if(tmp.equals(start))
{
allPath.add(new ArrayList<>(list));
return;
}
if(!preWords.containsKey(tmp))
return;
for (String str : preWords.get(tmp)) {
list.add(0,str);
backTraverse(str, start);
list.remove(0);
}
}
}