渣中渣這次只能上IDE了,代碼太長了,老有筆誤。結果還調試了半天,還超時,最後改了一上午才過,坑爹啊,面試要是面到這個題,哥們就讓你們見識見識什麼叫翔尿齊飛!996ms險過.......
public class Solution {
HashSet<String> dictmap;
HashSet<String> rSet = new HashSet<String>();
ArrayList<ArrayList<String>> reslist = new ArrayList<ArrayList<String>>();
Character[] ch;
boolean debug = false;
public ArrayList<String> getNext(String ts) {
ArrayList<String> candis = new ArrayList<String>();
char[] ss = ts.toCharArray();
String res = null;
for (int j = 0; j < ss.length; j++) {
ss = ts.toCharArray();
for (int i = 0; i < ch.length; i++) {
ss[j] = ch[i];
res = String.valueOf(ss);
if (dictmap.contains(res) && !res.equals(ts)) {
candis.add(res);
}
}
}
// if (debug)
// System.out.println("get next form [" + ts + "] as " + candis);
return candis;
}
public ArrayList<ArrayList<String>> findLadders(String start, String end,
HashSet<String> dict) {
dict.remove(start);
dict.add(end);
dictmap = dict;
ch = new Character[26];
ch[0] = 'a';
for (int i = 0; i < ch.length; i++)
ch[i] = (char) (ch[0] + i);
dictBFS2(start, end);
return reslist;
}
private ArrayList<ArrayList<String>> dictBFS2(String start, String end) {
// this time we need to record the path they goes
ArrayList<String> ways = new ArrayList<String>();
// used to save the bfs level
ArrayList<Integer> steps = new ArrayList<Integer>();
// save the father position for result
HashMap<String, ArrayList<Integer>> fatherp = new HashMap<String, ArrayList<Integer>>();
// save where the end goes
ArrayList<Integer> endplace = new ArrayList<Integer>();
//
int currentStep = 0;
int cnumber = 0;
HashSet<String> stageSet=new HashSet<String>();
ways.add(start);
fatherp.put(start, null);
steps.add(1);
while (cnumber < ways.size()) {
// System.out.println("cnumber="+cnumber+" size="+ways.size());
String father = ways.get(cnumber);
ArrayList<String> sons = getNext(father);
for (String son : sons) {
if (son.equals(end)){
if(debug)
System.out.println("record end as"+cnumber);
endplace.add(cnumber);
ways.add(son);
}
else {
rSet.add(son);
ArrayList<Integer> tmplist = fatherp.get(son);
if (tmplist == null) {
tmplist = new ArrayList<Integer>();
fatherp.put(son, tmplist);
ways.add(son);
}
tmplist.add(cnumber);
if (debug)
System.out.println("son ["+son+"] add "+cnumber);
// dictmap.remove(son);
}
// System.out.println("adding "+son);
}
cnumber++;
if (cnumber == steps.get(currentStep)) {
if (endplace.size() > 0)
break;
if (debug)
System.out.println("next level");
dictmap.removeAll(rSet);
rSet.clear();
currentStep++;
steps.add(ways.size());
}
}
if(debug){
for(String sw:ways)
System.out.print(sw+" ");
System.out.println();
int i=0;
for(String sw:ways)
System.out.print(" "+i+++" ");
System.out.println();
}
// gen res1
{
Stack<String> sts = new Stack<String>();
sts.push(end);
HashSet<String> usedSet=new HashSet<String>();
for (int j : endplace) {
if (debug)
System.out.println("j="+ j);
if(usedSet.contains(ways.get(j)))
continue;
usedSet.add(ways.get(j));
genRes(ways, j, fatherp, sts);
// sts.remove(sts.size() - 1);
}
}
//gen res2
{
}
return reslist;
}
public void debuglist(Stack<String> sts){
if (debug){
System.out.print("[STS]: ");
for(int i=0;i<sts.size();i++)
System.out.print(sts.get(i)+ " ");
System.out.println();
}
}
private void genRes(ArrayList<String> ways, int place,
HashMap<String, ArrayList<Integer>> fatherp, Stack<String> sts) {
int ep = place;
String ts = ways.get(ep);
if (debug)
System.out.println("get place " + place + " as " + ts);
sts.push(ts);
debuglist(sts);
ArrayList<Integer> lis = fatherp.get(ts);
if (lis == null) {
ArrayList<String> resp = new ArrayList<String>();
for(int i=sts.size()-1;i>=0;i--)
resp.add(sts.get(i));
reslist.add(resp);
if (debug)
System.out.println();
String sst=sts.pop();
if (debug)
System.out.println("remove last as "+sst);
debuglist(sts);
return;
}
for (int j : lis) {
genRes(ways, j, fatherp, sts);
}
String sst=sts.pop();
if (debug)
System.out.println("remove in for last as "+sst);
debuglist(sts);
}
}
180行 看着想哭的心都有了。 其實這個題就是一個圖遍歷。
但要注意:
假設又M個單詞,每個單詞長度是L
1. 遍歷方式肯定是單詞到單詞,但是是先做好遍歷表呢,還是循環單詞呢?
這裏其實有個優化問題,如果每個單詞的lenth足夠長,就應該先取O(M2)獲得連通圖
這裏單詞都很短,乾脆26個字母循環試試,加上dict用hashmap實現,也不慢,時間應該是O(26*L*Map查找時間)但顯然這種比上一種M方的要快,所以ladder 1 還是2裏都是用的這種。
2. 然後就相對簡單了,就是個樹形圖(其實圖裏有環!!而且有很多種環,這就是Ladder2坑爹的原因之一)。Ladder1是求最短距離,不用考慮環的問題,BFS的時候每用一個節點就刪除一個dict裏的節點就好,爲什麼?因爲使用了環也不會獲得更短路徑。爲什麼?因爲BFS是層次遍歷,目前既然能遍歷到節點X,那麼之前的層次一定不能遍歷到節點X,X最短路徑一定是在這一層。即使本層有其他節點可能作爲X的父節點,對層次結構來說是不影響結果的。
所以Ladder1結果就出來了,某層出現end,那就返回層數就ok。
3. Ladder2就坑爹的多了,建立圖結構的時候有兩種構建方法需要注意,一種方式就是同層環可以化爲無環的兩個甚至多個節點,另一種就直接用帶環的樹結構。那就是對同層環的處理。因爲需要遍歷所有最短路徑,因此同層環不能被忽略。這時,整個處理過程被劃分爲兩部分,
1. 通過BFS尋找end
2. 找到end後,以end爲根節點反向遍歷路徑並寫出全部路徑。 這裏應該是個DFS
如果按照無環做法,即沒出現一次子節點就列一次子節點,會在生成結果的時候超時。 我沒太想明白爲什麼,估計是我的代碼裏有bug
渣又優化了一下,用了個數據結構,結果因爲String 和Node比較的bug改了20分鐘,請叫我渣中渣
優化後代碼960ms險過
HashMap<String, Node> dictmap = new HashMap<String, Node>();
HashSet<String> dictSet;
ArrayList<ArrayList<String>> reslist = new ArrayList<ArrayList<String>>();
Character[] ch;
boolean debug = false;
class Node {
String value;
ArrayList<Node> fathers = new ArrayList<Node>();
public Node(String s) {
value = s;
}
public void addFather(Node e) {
fathers.add(e);
}
@Override
public boolean equals(Object obj) {
Node t = (Node) obj;
return t.value == value;
}
@Override
public String toString() {
return value;
}
}
public ArrayList<Node> getNext(Node ts) {
ArrayList<Node> candis = new ArrayList<Node>();
char[] ss = ts.value.toCharArray();
String res = null;
for (int j = 0; j < ss.length; j++) {
ss = ts.value.toCharArray();
for (int i = 0; i < ch.length; i++) {
ss[j] = ch[i];
res = String.valueOf(ss);
if (dictSet.contains(res) && !res.equals(ts.value)) {
// any new gen node will added into dictmap
if (!dictmap.containsKey(res)) {
Node n = new Node(res);
dictmap.put(res, n);
}
Node n = dictmap.get(res);
n.addFather(ts);
candis.add(n);
}
}
}
return candis;
}
public ArrayList<ArrayList<String>> findLadders(String start, String end,
HashSet<String> dict) {
dict.remove(start);
dict.add(end);
dictSet = dict;
ch = new Character[26];
ch[0] = 'a';
for (int i = 0; i < ch.length; i++)
ch[i] = (char) (ch[0] + i);
dictBFS3(start, end);
return reslist;
}
private void dictBFS3(String start, String end) {
ArrayList<Node> nodelist = new ArrayList<Node>();
ArrayList<Integer> steps = new ArrayList<Integer>();
HashSet<Node> reachset = new HashSet<Node>();
HashSet<String> levelset = new HashSet<String>();
int currentStep = 0;
int l = 0;
nodelist.add(new Node(start));
steps.add(1);
while (l < nodelist.size()) {
Node tnode = nodelist.get(l);
ArrayList<Node> sonlist = getNext(tnode);
for (Node son : sonlist) {
// if have son, this should be the last step and save it's
// father in a set
if (son.value.equals(end)) {
reachset.add(tnode);
continue;
} else {
if (!levelset.contains(son.value)) {
levelset.add(son.value);
nodelist.add(son);
}
}
}
l++;
if (l == steps.get(currentStep)) {
if (reachset.size() > 0)
break;
dictSet.removeAll(levelset);
steps.add(nodelist.size());
currentStep++;
}
}
Stack<String> res = new Stack<String>();
res.push(end);
for (Node node : reachset) {
genRes(node, res, start);
}
}
public void genRes(Node node, Stack<String> res, String start) {
if (node.fathers.size() == 0) {
ArrayList<String> list = new ArrayList<String>();
list.add(start);
for (int i = res.size() - 1; i >= 0; i--)
list.add(res.get(i));
reslist.add(list);
return;
}
res.add(node.value);
for (Node tnode : node.fathers) {
genRes(tnode, res, start);
}
res.pop();
}
剛總結完,看一個神的blog說,這題對面試準備不大~~~~~ 我CCCCCCCC