Wednesday, March 8, 2017

Maximum Number in Mountain Sequence

Version#1(naiive)
我用的传统的二分查找方法,在mid点处分别求它的prevSlope和postSlope,这个过程中用一个slope函数来处理一些边界情况,防止mid 等于start 或 end 的时候会越界。
譬如如果去掉边界处理,在如下testcase中就会报错(虽然我觉得这个testcase并不严格符合题目的描述)。

public class Solution {
    /**
     * @param nums a mountain sequence which increase firstly and then decrease
     * @return then mountain top
     */
    public int mountainSequence(int[] nums) {
        // Write your code here
        if (nums == null || nums.length == 0) {
            return Integer.MAX_VALUE;
        }
        int start = 0;
        int end = nums.length - 1;
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (slope(mid, nums) > 0 && slope(mid + 1, nums) < 0) {
                return nums[mid];
            } else if (slope(mid, nums) > 0) {
                start = mid;
            } else {
                end = mid;
            }
        }
        if (slope(start, nums) > 0 && slope(start + 1, nums) < 0) {
            return nums[start];
        } else {
            return nums[end];
        }
    }
    private int slope(int node, int[] nums) {
        if (node == 0) {
            return 1;
        }
        if (node == nums.length) {
            return -1;
        }
        return nums[node] - nums[node - 1];
    }
}

Version#2
看了答案以后发现这道题实际上应该用三分法查找。关于三分法在网上找到了一篇讲解讲得很清楚。
三分查找适合用来查找最值。
三分查找
于是修改答案:
public class Solution {
    /**
     * @param nums a mountain sequence which increase firstly and then decrease
     * @return then mountain top
     */
    public int mountainSequence(int[] nums) {
        // Write your code here
        if (nums == null || nums.length == 0) {
            return Integer.MAX_VALUE;
        }
   
        int start = 0;
        int end = nums.length - 1;
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            int midmid = mid + (end - mid) / 2;
            if (nums[mid] > nums[midmid]) {
                end = midmid - 1;
            } else if (nums[mid] < nums[midmid]) {
                start = mid + 1;
            } else {
                start = mid;
                end = midmid;
            }
        }
        if (nums[start] > nums[end]) {
            return nums[start];
        } else {
            return nums[end];
        }
    }
}

助教您好:
(刚刚看到ladder里面新加了一些题目。。。结果后面的题被锁住做不了了, 好悲伤!)
我想请问这个http://www.jiuzhang.com/solutions/maximum-number-in-mountain-sequence/答案使用的三分法里面,
int m1 = left + (right - left) / 2;
int m2 = right - (right - m1) / 2;
我把这里m2展开以后发现是 right / 2 + (right + left) / 4, 不太理解它的意义是什么?
我自己的答案写的是:
int m2 = m1 + (right - m1) / 2, 代表着在中点和右端点之间再取一次中点,但是这样就在[1,2,4,8,6,3] 的testcase里面报错了。不知道是什么问题?
谢谢!
P.S.今天把我自己的答案改成int m2 = m1 + (right - m1) / 2 + 1 之后发现运行正确了。然而还是不理解这么取的意义是什么。。。
P.P.S 我发现这个问题发生在start 和end 中间只间隔一个数的情况下,譬如:
index 3 4 5
value 8 6 3
此时left = #3, right = #5, 所以m1 == m2 == #4, 就会造成 6是最大值的错误答案。而老师的标答中通过 right - (right - m1) / 2 这个公式,使得start 和 end中间只相隔一个数的情况下,m1 和 m2 也不会相等。 不知道我理解的对不对?
但是我感觉这个边界情况如果第一次做的话很难想得周全。。。不知道可不可以借鉴老师在二分法中的模板,把while 循环的条件改成 left + 2 < right就退出, 然后再进行判断,这样的做法是不是更加general一点呢?
另外很想问题下老师求解m2的公式如果是第一次做的话是怎么想出来的,思路是什么? 谢谢!

Version#3 我自己想的更改退出条件的做法
这个自己想出来的做法虽然看着有点丑陋但是思路非常直接,而且没有很多edge case需要考虑,感觉比较适合我。
写的过程中唯一一个bug是没有考虑到只有一个输入值的情况,这里在最前边加以讨论了。同理也可以放在最后当做 left == right的一个情况讨论。
public class Solution {
    /**
     * @param nums a mountain sequence which increase firstly and then decrease
     * @return then mountain top
     */
    public int mountainSequence(int[] nums) {
        // Write your code here
        if (nums == null || nums.length == 0) {
            return Integer.MAX_VALUE;
        }
        if (nums.length == 1) {
            return nums[0];
        }
        int left = 0;
        int right = nums.length - 1;
        while (left + 2 < right) {
            int mid = left + (right - left) / 2;
            int midmid = mid + (right - mid) / 2;
            if (nums[mid] < nums[midmid]) {
                left = mid + 1;
            } else if (nums[mid] > nums[midmid]) {
                right = midmid - 1;
            } else {
                left = mid + 1;
                right = midmid - 1;
            }
        }
        int tempMax = Math.max(nums[left], nums[right]);
        if (right == left + 1) {
            return tempMax;
        } else {
            return Math.max(tempMax, nums[left + 1]);
        }
    }
}

Tuesday, March 7, 2017

Maximum Subarray

还是做得复杂了一点,跟上一道sliding window的问题一样
不需要先把整个array扫一遍记录一个PrefixSum的数组,只需要维持一个int PrefixSum就可以了
像这种从左向右扫一遍的题,都不需要提前做好数组。

public class Solution {
    /**
     * @param nums: A list of integers
     * @return: A integer indicate the sum of max subarray
     */
    public int maxSubArray(int[] nums) {
        // write your code
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int[] prefixSum = new int[nums.length + 1];
        prefixSum[0] = 0;
        for (int i = 1; i < prefixSum.length; i++) {
            prefixSum[i] = prefixSum[i - 1] + nums[i - 1];
        }
        int prevMinSum = prefixSum[0];
        int maxSum = prefixSum[1];
        for (int j = 1; j < prefixSum.length; j++) {
            prevMinSum = Math.min(prevMinSum, prefixSum[j - 1]);
            maxSum = Math.max(maxSum, prefixSum[j] - prevMinSum);
        }
        return maxSum;
     
    }
}

Window Sum

int[] result = new int[0]; //is allowed
It is for reducing null checks. You can iterate on an empty array but you can't iterate on null.
Reference from Jiuzhang https://www.jiuzhang.com/solutions/window-sum/
这道题我理所当然地用了PrefixSum的方法,先把整个数组遍历一遍求出PrefixSum,然后再遍历一遍求sum。
然而实际上这道题不用这么复杂,看了九章的解法,就是直接求出前k个数的和,然后遍历的过程中每次加入后一个数并减去前一个数,这样只要把整个数组遍历一遍即可。非常简单。

My own code:
public class Solution {
    /**
     * @param nums a list of integers.
     * @return the sum of the element inside the window at each moving.
     */
    public int[] winSum(int[] nums, int k) {
        // write your code here
        if (nums == null || nums.length == 0 || k <= 0) {
            return new int[0];
        }
        int[] prefixSum = new int[nums.length + 1];
        prefixSum[0] = 0;
        int sum = 0;
        for (int i = 1; i < prefixSum.length; i++) {
            sum += nums[i - 1];
            prefixSum[i] = sum;
        }
        int[] result = new int[nums.length - k + 1];
        for (int j = 0; j < result.length; j++) {
            result[j] = prefixSum[j + k] - prefixSum[j];
        }
        return result;
    }
}

Friday, March 3, 2017

126. Word Ladder II

四刷 06/2022
首先搞懂了为什么要BFS+DFS,因为BFS没有办法记录路径
在第一遍BFS的时候用HashSet记录了每个点的previous node,这里有情况就是上一层有可能同时有两个parent都指向当前node,同时也有可能当前层有邻居node指向当前node
如果是邻居node指向当前node就不满足shortest path的需求
我的解决办法是每一个layer traverse的时候新建一个hashmap,而检查是否visited的时候只检查之前的hashmap,这样保证了两个parent node pop出来的当前node都可以被记录下来,最后当前层结束之后再把层map加入到最终的previous map里面

Time O(N*K^2) -> N is the number of nodes, traverse the graph with BFS will use O(N) time
K is the word length. While traversing each node, we take O(26 K) time to replace each character of the word and use O(K) time to generate the string to check from the hashset, so that's O(N* K^2)
The backward dfs traverse won't take more than O(N) time
Space O(NK) for the set to store the words and the queue for BFS
Runtime: 17 ms, faster than 61.93% of Java online submissions for Word Ladder II.
Memory Usage: 43.4 MB, less than 76.61% of Java online submissions for Word Ladder II.
class Solution {
    public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
        // Do a level order traversal from beginWord to endWord
        // Use a map to keep track of each word's previous word in the graph
        // Once we found the endWord, we search backwards using the previous pointer and record the path
        Set<String> wordSet = new HashSet<>(wordList);
        // prev is also used as visited map
        Map<String, Set<String>> prev = new HashMap<>();
        Queue<String> que = new ArrayDeque<>();
        que.offer(beginWord);
        boolean found = false;
        List<List<String>> result = new ArrayList<>();
        while (!que.isEmpty() && !found) {
            int size = que.size();
            Map<String, Set<String>> temp = new HashMap<>();
            while (size-- > 0) {
                String curr = que.poll();
                for (String next : neighbors(curr, wordSet)) {
                    // System.out.printf("curr:%s,next:%s,prevMap:%s\n", curr, next, prev.toString());
                    if (prev.containsKey(next)) {
                        continue;
                    }
                    if (next.equals(endWord)) {
                        found = true;
                    }
                    temp.putIfAbsent(next, new HashSet<>());
                    temp.get(next).add(curr);
                    que.offer(next);
                }
            }
            prev.putAll(temp);
        }
        dfs(prev, beginWord, result, new LinkedList<>(), endWord);
        return result;
    }
    
    private void dfs(Map<String, Set<String>> map, String beginWord, List<List<String>> result, List<String> path, String word) {
        path.add(0, word);
        if (word.equals(beginWord)) {
            result.add(new ArrayList<>(path));
            path.remove(0);
            return;
        }
        for (String prev : map.getOrDefault(word, new HashSet<>())) {
            dfs(map, beginWord, result, path, prev);
        }
        path.remove(0);
    }
    
    private List<String> neighbors(String word, Set<String> wordSet) {
        // replace each character of the word and check if that new word is in the word set
        List<String> result = new ArrayList<>();
        char[] chars = word.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            char originalChar = chars[i];
            for (char c = 'a'; c <= 'z'; c++) {
                if (c == originalChar) {
                    continue;
                }
                chars[i] = c;
                String nWord = new String(chars);
                if (wordSet.contains(nWord)) {
                   result.add(nWord); 
                }
            }
            chars[i] = originalChar;
        }
        // System.out.printf("from:%s, to:%s\n", word, result.toString());
        return result;
    }
}


三刷
基本上是 build + BFS + DFS 一遍写出来的,写了大概40分钟
三个bug
1没有把 beginWord加入到wordList和words set里面,导致graph里面没有beginWord
2写BFS的时候考虑到了可能无法走到endword的情况并且返回null,但是main method里面忘记check了。。。 org
导致了一个NullPointerException
3DFS里面的neighbor有可能虽然是当前node的neighbor,但是无法从beginword到达
想了一下为什么会有这个情况
因为BFS为了优化,在到达endWord之后就停止了,这样所有比endWord距离更远的点都没有加入到map里面
这样会导致比endWord远的connected component没有值
所以需要特殊check一下
我这里用的Integer.equals() 来包含了Null的情况

54.86 %
class Solution {
    public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
        // build graph
        // two words can be connected if they only have 1 diff character
        // start bfs from start to end and generate shorted steps from start to all nodes
        // dfs from end to start, each time only step into node that are 1 step nearer to start
        List<List<String>> result = new ArrayList<>();
        wordList.add(beginWord);
        Set<String> words = new HashSet<>(wordList);
        if (!words.contains(endWord)) return result;
        Map<String, Set<String>> graph = buildGraph(wordList, words);
        Map<String, Integer> map = getSP(beginWord, endWord, graph); // shortest path from beginWord
        if (map == null) return result;
        List<String> path = new LinkedList<>();
        path.add(endWord);
        dfs(endWord, beginWord, endWord, graph, map, path, result);
        return result;
    }
   
   
    private void dfs(String word, String beginWord, String endWord, Map<String, Set<String>> graph, Map<String, Integer> map, List<String> path, List<List<String>> result) {
        // System.out.println(word);
        if (word.equals(beginWord)) {
            result.add(new ArrayList<>(path));
            return;
        }
        Integer dist = map.get(word) - 1;
        // given a word, trying to reach beginWord
        Set<String> neighbors = graph.get(word);
        for (String neighbor : neighbors) {
            if (dist.equals(map.get(neighbor))) { // in case thie neighbor can't be reached by beginWord
                path.add(0, neighbor);
                dfs(neighbor, beginWord, endWord, graph, map, path, result);
                path.remove(neighbor);
            }
        }
    }
   
   
    private Map<String, Integer> getSP(String beginWord, String endWord, Map<String, Set<String>> graph) {
        Map<String, Integer> map = new HashMap<>(); // shortest path from beginWord
        Queue<String> que = new LinkedList<>();
        Set<String> visited = new HashSet<>();
        visited.add(beginWord);
        que.offer(beginWord);
        int step = 0;
        while (!que.isEmpty()) {
            int size = que.size();
            while (size-- > 0) {
                String curr = que.poll();
                // System.out.println(curr + " step " + step);
                map.put(curr, step);
                if (curr.equals(endWord)) return map;
                Set<String> neighbors = graph.get(curr);
                for (String neighbor : neighbors) {
                    if (visited.add(neighbor)) {
                        que.offer(neighbor);
                    }
                }
            }
            step++;
        }
        return null;
    }
   
    private Map<String, Set<String>> buildGraph(List<String> wordList, Set<String> words) {
        Map<String, Set<String>> graph = new HashMap<>();
        for (String word : wordList) {
            Set<String> neighbors = new HashSet<>();
            char[] chars = word.toCharArray();
            for (int i = 0; i < chars.length; i++) {
                char c = chars[i];
                for (char t = 'a'; t <= 'z'; t++) {
                    if (t != c) {
                        chars[i] = t;
                        String str = new String(chars);
                        if (words.contains(str)) {
                            neighbors.add(str);
                        }
                    }
                }
                chars[i] = c;
            }
            graph.put(word, neighbors);
        }
        return graph;
    }
}



二刷 9/17
(看了一下一刷的答案,感觉像傻逼一样啊,第一遍bfs记录的是每个点和start点的距离?九章老师当时讲的时候是不是脑子瓦特了)

写了一个5 + 25 + 25 + 10 mins
总的来说感觉思路很清楚
bug1 忘记了把Neighbors加入到queue里面
bug2 在buildGraph里面判断能不能最终走到endPoint。一开始判断没有endPoint的条件是graph.size() == 0,有错。想了一下发现因为graph是一边走一边build的,所以即使没有找到也是有size的,应该在buildGraph最后return之前看一下boolean found,如果没有found就直接返回null

我自己感觉难点在于level order traversal的时候
如果在一层遇到了一个点,不能立刻把它从set里面去掉,因为上一层的其他点也可能访问到它,所以应该先全部暂时用一个set存下来,这一层结束后才能统一从wordSet里面去掉。因为如果之后层再访问就不是最短路径了

85.73 %
class Solution {
    public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
        List<List<String>> result = new ArrayList<>();
        if (beginWord == null || endWord == null) return result;
        // bfs to find the shortest path and construct the graph
        // key-String of word, value-List<String>-neighbors of its previous layer
        Map<String, List<String>> graph = buildGraph(beginWord, endWord, wordList); //TODO
     
        if (graph != null && graph.size() != 0) {
            // start from endWord, do dfs search until reach the beginWord point, keep track of paths
            findPath(beginWord, endWord, graph, new ArrayList<>(), result);
        }
        return result;
    }
 
    private Map<String, List<String>> buildGraph(String beginWord, String endWord, List<String> wordList) {
        Map<String, List<String>> graph = new HashMap<>();
        Queue<String> que = new LinkedList<>();
     
        // Convert wordList into wordSet, to reduce time complexity
        Set<String> wordSet = new HashSet<>();
        for (String word : wordList) wordSet.add(word);
     
        que.offer(beginWord);
        boolean found = false;
        while (!que.isEmpty() && wordSet.size() != 0 && !found) {
            int size = que.size();
            Set<String> visited = new HashSet<>();
            // travel level by level
            while (size-- > 0) {
                String curr = que.poll();
                char[] chars = curr.toCharArray();
                // getNeighbors
                for (char newChar = 'a'; newChar <= 'z'; newChar++) {
                    for (int i = 0; i < curr.length(); i++) {
                        if (newChar != chars[i]) {
                            char oldChar = chars[i];
                            chars[i] = newChar;
                            String newWord = new String(chars);
                            if (wordSet.contains(newWord)) {
                                if (!graph.containsKey(newWord)) graph.put(newWord, new ArrayList<>());
                                graph.get(newWord).add(curr);
                                if (!visited.contains(newWord)) {
                                    visited.add(newWord);
                                    que.offer(newWord);
                                }
                            }
                            // Set back
                            chars[i] = oldChar;
                        }
                    }
                }
            }
            // delete this layer from wordList
            for (String s : visited) {
                wordSet.remove(s);
                if (s.equals(endWord)) found = true;
            }
        }
        if (found) return graph;
        else return null;
    }
 
    private void findPath(String beginWord, String currWord, Map<String, List<String>> graph, List<String> path, List<List<String>> result) {
        path.add(currWord);
        if (currWord.equals(beginWord)) {
            List<String> subResult = new ArrayList<>();
            for (int i = path.size() - 1; i >= 0; i--) {
                subResult.add(path.get(i));
            }
            result.add(subResult);
        } else {
            List<String> prevNeighbors = graph.get(currWord);
            for (String prevNeighbor : prevNeighbors) {
                findPath(beginWord, prevNeighbor, graph, path, result);
            }
        }
        path.remove(path.size() - 1);
    }
}


一刷
LintCode Version
这道题在知道思路的情况下还是用了3个多小时才写出来,耗时很长。
整体思路是先用bfs从end点找到start点, 得到一个中间点到end距离的HashMap。
然后从start点进行dfs,每次都只对距离有所缩短的点进行搜索,防止进行无用的搜索,大大节省了运行时间。
主要有这样几个bug:
1. HashMap<String, int> 这种写法是错误的, HashMap里面不能有primitive type。要写成HashMap<String, Integer>
2. 有一个地方两个String的比较写成了“==”, 这样属于比较了两个String的reference, 而不是值相等。这里要求的是值相等,所以必须写成“string1.equals(string2)”的形式
3. 默认start和end是不包含在dictionary里面的。刚开始写的时候只考虑到要把end加入到dict里面。但是在bfs运行中,从end点搜索到start点,此时如果start点不在dict里面,就不会被作为任何点的neighbor,造成distance的HashMap里面start点的value是null。而dfs调用distance.get(start)的时候就会报NullPointerException。所以必须在初始时将start和end都加入到dict里面。

public class Solution {
    /**
      * @param start, a string
      * @param end, a string
      * @param dict, a set of string
      * @return a list of lists of string
      */
    public List<List<String>> findLadders(String start, String end, Set<String> dict) {
        // write your code here
        List<List<String>> result = new ArrayList<>();
        if (start.equals(end)) {
            List<String> temp = new ArrayList<>();
            temp.add(start);
            result.add(temp);
            return result;
        }
        if (dict == null) {
            return result;
        }
        dict.add(start);
        dict.add(end);
     
        //A bfs function which returns a hashmap
        //k - string of the word
        //value - distance to the end word
        Map<String, Integer> distance = bfs(start, end, dict);
        //call the dfs function recursively
        //add paths to result
        List<String> path = new ArrayList<>();
        path.add(start);
        dfs(end, start, path, result, dict, distance);
        return result;
    }
 
    public void dfs(String end, String word, List<String> path,
                    List<List<String>> result, Set<String> dict,
                    Map<String, Integer> distance) {
        if (word.equals(end)) {
            result.add(new ArrayList<String>(path));
            return;
        }
        List<String> neighbors = getNeighbors(word, dict);
        //System.out.println(neighbors);
        for (String neighbor : neighbors) {
            //System.out.println(word + " " + neighbor + " " + distance.get(word) + " " + distance.get(neighbor));
            if (distance.get(word) == distance.get(neighbor) + 1) {
                path.add(neighbor);
                //System.out.println("path " + path);
                dfs(end, neighbor, path, result, dict, distance);
                path.remove(path.size() - 1);
            }
        }
     
    }
 
    public Map<String, Integer> bfs(String start, String end, Set<String> dict) {
        Map<String, Integer> distance = new HashMap<String, Integer>();
        for (String word : dict) {
            distance.put(word, Integer.MAX_VALUE);
        }
     
        Queue<String> que = new LinkedList<>();
        Set<String> visited = new HashSet<>();
     
        que.offer(end);
        visited.add(end);
        int length = 1;
        distance.put(end, length);
        while(!que.isEmpty()) {
            int size = que.size();
            length++;
            for (int i = 0; i < size; i++) {
                String curr = que.poll();
                List<String> neighbors = getNeighbors(curr, dict);
                for (String neighbor : neighbors) {
                    if (neighbor.equals(start)) {
                        //System.out.println("start: " + neighbor);
                        distance.put(neighbor, length);
                        //System.out.println("length: " + distance.get(start));
                        return distance;
                    }
                    if (visited.contains(neighbor) || que.contains(neighbor)) {
                        continue;
                    }
                    distance.put(neighbor, length);
                    visited.add(neighbor);
                    que.offer(neighbor);
                }
            }
        }
        return distance;
    }
    public List<String> getNeighbors(String word, Set<String> dict) {
        List<String> neighbors = new ArrayList<>();
        for (int  k = 0; k < word.length(); k++) {
            for (char c = 'a'; c <= 'z'; c++) {
                if (c == word.charAt(k)) {
                    continue;
                }
                String newWord = word.substring(0, k) + c + word.substring(k + 1);
                if (dict.contains(newWord)) {
                    neighbors.add(newWord);
                }
            }
        }
        return neighbors;
    }
}

Thursday, March 2, 2017

127. Word Ladder

A very useful blog

三刷 05/2022
Version #1 BFS
Time complexity
Each node is visited once by BFS O(n) -> n is #nodes
For each node, find neighbors O(26L^2) ~ O(L^2) -> L is the length of word [new String(chars) takes O(L) time]
Time O(n*L^2)
Space O(n) -> set for all nodes
Runtime: 113 ms, faster than 65.33% of Java online submissions for Word Ladder.
Memory Usage: 82.6 MB, less than 40.89% of Java online submissions for Word Ladder.
class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        // a.check if two words are adjacent
        //  Selet two words O(n^2)
        //  Copare O(wordLen)
        //  Total: O(wordLen*n^2)
        // b.enumerate all possible transformation of each word
        //  Selet one word O(n)
        //  Replace each character by 26 characters O(wordLen * 26)
        //  Check if that transformation is in the wordList O(1) -> use set
        //  Total: O(26*wordLen*n)
        // If wordList is greater than 26, we should use method b
        
        Set<String> wordSet = new HashSet<>(wordList);
        // Do breadth first search on the beginWord
        Queue<String> que = new ArrayDeque<>();
        que.offer(beginWord);
        int distance = 1;
        while (!que.isEmpty()) {
            int size = que.size();
            distance++;
            while (size-- > 0) {
                String curr = que.poll();
                for (String neighbor : findNeighbors(curr, wordSet)) {
                    // System.out.printf("(%s)neighbor(%s)\n", curr, neighbor);
                    if (neighbor.equals(endWord)) {
                        return distance;
                    }
                    que.offer(neighbor);
                }
            }
        }
        return 0;
    }
    
    private List<String> findNeighbors(String curr, Set<String> wordSet) {
        List<String> neighbors = new ArrayList<>();
        char[] chars = curr.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            char prevChar = chars[i];
            for (char c = 'a'; c <= 'z'; c++) {
                if (c == prevChar) {
                    continue;
                }
                chars[i] = c;
                String temp = new String(chars);
                if (wordSet.contains(temp)) {
                    neighbors.add(temp);
                    wordSet.remove(temp);
                }
            }
            chars[i] = prevChar;
        }
        return neighbors;
    }
}

二刷
BFS
Time O(size of wordlist)
Space O(size of wordlist)

71.10 % 
public class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        if (beginWord == null || endWord == null || wordList == null) return 0;
        if (beginWord.equals(endWord)) return 1;
        Set<String> visited = new HashSet<>();
        Set<String> dict = new HashSet<>();
        for (String word : wordList) {
            dict.add(word);
        }
        Queue<String> que = new LinkedList<>();
        que.add(beginWord);
        visited.add(beginWord);
        int step = 0, size;
        String currStr;
        List<String> neighbors;
        while (!que.isEmpty()) {
            step++;
            size = que.size();
            for (int i = 0; i < size; i++) {
                currStr = que.poll();
                neighbors = getNeighbors(currStr, dict);
                for (String neighbor : neighbors) {
                    if (neighbor.equals(endWord)) return step + 1;
                    if (visited.contains(neighbor)) continue;
                    que.offer(neighbor);
                    visited.add(neighbor);
                }
            }
        }
        return 0;
    }
    //Given a word, return all of its neighbors(have not been visited yet) in the graph as a list
    private List<String> getNeighbors(String currStr, Set<String> dict) {
        List<String> neighbors = new ArrayList<>();
        char[] cArray = currStr.toCharArray();
        for (int i = 0; i < currStr.length(); i++) {
            //The original character
            char temp = cArray[i];
            for (char sub = 'a'; sub <= 'z'; sub++) {
                if (sub != temp) {
                    cArray[i] = sub;
                    String newStr = new String(cArray);
                    if (dict.contains(newStr)) {
                        neighbors.add(newStr);
                    }
                }
            }
            //backtracking
            cArray[i] = temp;
        }
        //System.out.println(neighbors);
        return neighbors;
    }
}



一刷
public class Solution {
    /**
      * @param start, a string
      * @param end, a string
      * @param dict, a set of string
      * @return an integer
      */
    public int ladderLength(String start, String end, Set<String> dict) {
        // write your code here
     
        int sp = 0;
        if (dict == null  || dict.size() == 0) {
            return sp;
        }
        if (start.equals(end)) {
            return 1;
        }
        //it is possible that the dict doesn't contain the end word
        dict.add(end);
        sp = bfs(start, end, dict);
        return sp;
    }
    public int bfs(String start, String end, Set<String> dict) {
        int sp = 1;
        Queue<String> que = new LinkedList<>();
        Set<String> visited = new HashSet<>();
        que.offer(start);
        visited.add(start);
        while(!que.isEmpty()) {
            int size = que.size();
            sp++;
            for (int i = 0; i < size; i++) {
                String curr = que.poll();
                List<String> neighbors = getNeighbors(curr, dict);
                for (String neighbor : neighbors) {
                    if (neighbor.equals(end)) {
                        return sp;
                    }
                    if (visited.contains(neighbor)) {
                        continue;
                    }
                    if (que.contains(neighbor)) {
                        continue;
                    }
                    que.add(neighbor);
                    visited.add(neighbor);
                }
            }
        }
        return sp;
    }
    public List<String> getNeighbors(String word, Set<String> dict) {
        List<String> neighbors = new ArrayList<>();
        for (int k = 0; k < word.length(); k++) {
            for (char c = 'a'; c < 'z'; c++ ) {
                if (c != word.charAt(k)) {
                    String newWord = word.substring(0, k) + c + word.substring(k + 1);
                    if (dict.contains(newWord)) {
                        neighbors.add(newWord);
                    }
                }
            }
        }
        //System.out.println(neighbors);
        return neighbors;
    }
}

Wednesday, March 1, 2017