Word Ladder Leetcode

Given two words (beginWord and endWord), and a dictionary's word list, find the length of shortest transformation sequence from beginWordto endWord, such that:

  1. Only one letter can be changed at a time.
  2. Each transformed word must exist in the word list. Note that beginWord is not a transformed word.

For example,

Given:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log","cog"]

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.
  • You may assume no duplicates in the word list.
  • You may assume beginWord and endWord are non-empty and are not the same.

 

UPDATE (2017/1/20):
The wordList parameter had been changed to a list of strings (instead of a set of strings). Please reload the code definition to get the latest changes.

 

这道题我先用了dfs,对于小数据是work的,但是一旦wordlist变长,就会超时。我才想到,最短路径应该用bfs啊,这么基本的概念怎么都忘记了呢。。。= =bfs做多了都做傻了。。。所以遇到题目先判断用什么方法也是很重要的。
dfs,bfs都写了。。如果wordlist太大,都会超时。。。
dfs:
public class Solution {
    int count = Integer.MAX_VALUE;
    Set<String> used;
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        if (wordList == null || wordList.size() == 0) {
            return count;
        }
        used = new HashSet<>();
        helper(beginWord, endWord, wordList, 1);
        return count == Integer.MAX_VALUE ? 0 : count;
    }
    private void helper(String beginWord, String endWord, List<String> wordList, int c) {
        if (beginWord.equals(endWord)) {
            count = Math.min(count, c);
        }
        for (String word : wordList) {
            if (used.contains(word)) {
                continue;
            }
            if (trans(beginWord, word)) {
                used.add(word);
                helper(word, endWord, wordList, c + 1);
                used.remove(word);
            }
        }
    }
    private boolean trans(String a, String b) {
        int l = a.length();
        for (int i = 0; i < a.length(); i++) {
            if (a.charAt(i) != b.charAt(i)) {
                l--;
            }
        }
        return l == a.length() - 1;
    } 
}

bfs:

public class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        if (wordList == null || wordList.size() == 0) {
            return 0;
        }
        Set<String> used = new HashSet<>();
        Queue<String> q = new LinkedList<>();
        q.offer(beginWord);
        Set<String> hs = new HashSet<>();
        hs.add(beginWord);
        return helper(endWord, wordList, q, hs);
    }
    private int helper(String endWord, List<String> wordList,Queue<String> q, Set<String> hs) {
        int count = 0;
        while (!q.isEmpty()) {
            int size = q.size();
            for (int i = 0; i < size; i++) {
                String s = q.poll();
                if (s.equals(endWord)) {
                    return count + 1;
                }
                for (String word : wordList) {
                    if (hs.contains(word)) {
                        continue;
                    }
                    if (trans(s, word)) {
                        q.offer(word);
                        hs.add(word);
                    }
                }
            }
            count++;
        }
        return 0;
    }
    private boolean trans(String a, String b) {
        int l = a.length();
        for (int i = 0; i < a.length(); i++) {
            if (a.charAt(i) != b.charAt(i)) {
                l--;
            }
        }
        return l == a.length() - 1;
    } 
}

把判断重构词的方法改变了之后用bfs终于可以通过了。。。

public class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        if (wordList == null || wordList.size() == 0) {
            return 0;
        }
        Set<String> words = new HashSet<>(wordList);
        Queue<String> q = new LinkedList<>();
        q.offer(beginWord);
        Set<String> used = new HashSet<>();
        used.add(beginWord);
        return helper(endWord, words, q, used);
    }
    private int helper(String endWord, Set<String> wordList, Queue<String> q, Set<String> hs) {
        int count = 0;
        while (!q.isEmpty()) {
            int size = q.size();
            for (int i = 0; i < size; i++) {
                String s = q.poll();
                if (s.equals(endWord)) {
                    return count + 1;
                }
                for (int j = 0; j < s.length(); j++) {
                    char[] chars = s.toCharArray();
                    for (char c = 'a'; c <= 'z'; c++) {
                        chars[j] = c;
                        String trans = new String(chars);
                        if (wordList.contains(trans)) {
                            if (hs.add(trans)) {
                                q.offer(trans);
                            }
                        }
                    }
                }
            }
            count++;
        }
        return 0;
    }
}

用了双边bfs,快了很多,从134ms变成39ms。思想就是两边同时开始找,那边扩张的范围小就从那边继续开始,这样就会比从一边开始扩张找到终点要快很多。

public class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        if (wordList == null || wordList.size() == 0) {
            return 0;
        }
        Set<String> words = new HashSet<>(wordList);
        if (!words.contains(endWord)) {
            return 0;
        }
        Set<String> beginSet = new HashSet<>();
        Set<String> endSet = new HashSet<>();
        Set<String> used = new HashSet<>();
        
        beginSet.add(beginWord);
        endSet.add(endWord);
        used.add(beginWord);
        used.add(endWord);
        
        return helper(beginSet, endSet, words, used);
    }
    private int helper(Set<String> beginSet, Set<String> endSet, Set<String> wordList, Set<String> used) {
        int count = 0;
        while (!beginSet.isEmpty()) {
            if (beginSet.size() > endSet.size()) {
                Set<String> tmp = beginSet;
                beginSet = endSet;
                endSet = tmp;
            }
            Set<String> nextLevel = new HashSet<>();
            for (String s : beginSet) {
                char[] chars = s.toCharArray();
                for (int j = 0; j < s.length(); j++) {
                    for (char c = 'a'; c <= 'z'; c++) {
                        char old = chars[j];
                        chars[j] = c;
                        String trans = new String(chars);
                        if (endSet.contains(trans)) {
                            return count + 2;
                        }
                        if (wordList.contains(trans)) {
                            if (used.add(trans)) {
                                nextLevel.add(trans);
                            }
                        }
                        chars[j] = old;
                    }
                }
            }
            beginSet = nextLevel;
            count++;
        }
        return 0;
    }
}

而且可以每次把char再还原,就不用新建啦。今天发现每次leetcode跑出来的时间是不一样的。。居然纠结了好久。。。

还有种算法是不用新建一个used的set,就每次从wordlist里面移除用过的词。号称运用了Dijkstra's algorithm算法,虽然我也不知道这个算法是啥。。。懒得查了。。。

public class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        if (wordList == null || wordList.size() == 0) {
            return 0;
        }
        Set<String> words = new HashSet<>(wordList);
        if (!words.contains(endWord)) {
            return 0;
        }
        Set<String> beginSet = new HashSet<>();
        Set<String> endSet = new HashSet<>();
        Set<String> used = new HashSet<>();
        
        beginSet.add(beginWord);
        endSet.add(endWord);
        words.remove(beginWord);
        words.remove(endWord);
        
        return helper(beginSet, endSet, words);
    }
    private int helper(Set<String> beginSet, Set<String> endSet, Set<String> wordList) {
        int count = 0;
        while (!beginSet.isEmpty()) {
            if (beginSet.size() > endSet.size()) {
                Set<String> tmp = beginSet;
                beginSet = endSet;
                endSet = tmp;
            }
            Set<String> nextLevel = new HashSet<>();
            for (String s : beginSet) {
                char[] chars = s.toCharArray();
                for (int j = 0; j < s.length(); j++) {
                    for (char c = 'a'; c <= 'z'; c++) {
                        char old = chars[j];
                        chars[j] = c;
                        String trans = new String(chars);
                        if (endSet.contains(trans)) {
                            return count + 2;
                        }
                        if (wordList.contains(trans)) {
                            nextLevel.add(trans);
                            wordList.remove(trans);
                        }
                        chars[j] = old;
                    }
                }
            }
            beginSet = nextLevel;
            count++;
        }
        return 0;
    }
}

这道题断断续续磕磕绊绊做了这么久。。。终于结束了。。。= =

posted @ 2017-03-25 13:37  璨璨要好好学习  阅读(347)  评论(0编辑  收藏  举报