两个单词间的最短路径(单源最短路径/相邻单词)Word Ladder

问题:

Given two words (beginWord and endWord), and a dictionary's word list, find the length of shortest transformation sequence from beginWord to 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.

解决:

【题意】和当前单词相邻的单词是:对当前单词改变一个字母且在字典中存在的单词。

① 这个就是图论算法中的单源最短路, 求单源最短路比较通用的算法是BFS和Dijkstra, 其区别是BFS不能用于带权重的图中, 而后者可以, 可以看出在本题中两个字符串之间是无权重的, 也就是如果连通就是1, 不联通就是无穷. BFS和Dijkstra的区别是前者的时间复杂度是O(n), 后者最多优化到O(m log n), 所以如果条件成立一般选择BFS要更好.

以题目中的例子为例:

  level = 1    hit   dict = [hot, dot, dog, lot, log,cog]
         ait bit cit ... xit yit zit ,  hat hbt hct ... hot ... hxt hyt hzt ,  hia hib hic ... hix hiy hiz
  level = 2    hot  dict = [dot, dog, lot, log,cog]
         aot bot cot dot ...  lot ... xot yot zot,hat hbt hct ... hxt hyt hzt,hoa hob hoc ... hox hoy hoz
  level = 3    dot lot  dict = [dog,log,cog]
         aot bot ... yot zot,dat dbt ...dyt dzt,doa dob ... dog .. doy doz,
         aot bot ... yot zot,lat lbt ... lyt lzt,loa lob ... log... loy loz
  level = 4   dog log dict = [cog] 
         aog bog cog
  level = 5   cog  dict = []

class Solution {//99ms
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        if(beginWord == null || endWord == null || beginWord.length() == 0 || endWord.length() == 0){
            return 0;
        }
        Set<String> set = new HashSet<>(wordList);//去重,并且去除了beginWord相同的单词。
        if(set.contains(beginWord)){
            set.remove(beginWord);
        }

        Queue<String> queue = new LinkedList<>();
        int level = 1;
        int curnum = 1; //当前层待处理的节点数
        int nextnum = 0;//记录下一层节点数
        queue.offer(beginWord);
        while(! queue.isEmpty()){
            String word = queue.poll();
            curnum --;
            for (int i = 0;i < word.length() ;i ++ ) {//更换单词
                char[] wchar = word.toCharArray();
                for (char j = 'a';j <= 'z' ;j ++ ) {
                    wchar[i] = j;
                    String tmp = new String(wchar);
                    if(set.contains(tmp)){
                        if(tmp.equals(endWord)){
                            return level + 1;
                        }
                        nextnum ++;
                        queue.add(tmp);
                        set.remove(tmp);//要将遍历到的单词从字典中删除
                    }
                }
            }
            if(curnum == 0){
                curnum = nextnum;
                nextnum = 0;
                level ++;
            }
        }
        return 0;
    }
}

② 对上面的方法进行优化,如果我们从两头扫,扫到中间任何一个word能够串联起来都可以,如果没有找到可以串联的word,那么返回0。

class Solution {//23ms
    public int ladderLength(String beginWord, String endWord, List<String> wordList){
        if (wordList == null || wordList.size() == 0){
            return 0;
        }
        Set<String> set = new HashSet<>(wordList);
        if (! set.contains(endWord)) return 0;//【注意】endWord是在字典里的,两端查找需要额外判断
        if (beginWord.equals(endWord)) return 1;
        Set<String> beginSet = new HashSet<>();
        Set<String> endSet = new HashSet<>();
        beginSet.add(beginWord);
        set.remove(beginWord);
        endSet.add(endWord);
        set.remove(endWord);
        return twoEndBFS(beginSet,endSet,set,2);
    }
    public int twoEndBFS(Set<String> beginSet,Set<String> endSet,Set<String> set,int level){
        if (beginSet.isEmpty() || endSet.isEmpty()) return 0;
        if (beginSet.size() > endSet.size()) return twoEndBFS(endSet,beginSet,set,level);
        Set<String> tmp = new HashSet<>();//作为从当前层开始的beginSet
        for (String word : beginSet){
            char[] wchar = word.toCharArray();
            for (int i = 0;i < wchar.length;i ++){
                char c = wchar[i];
                for (char j = 'a';j <= 'z';j ++){
                    wchar[i] = j;
                    String newWord = new String(wchar);
                    if (endSet.contains(newWord)) return level;//可以接上
                    if (set.contains(newWord)){
                        set.remove(newWord);
                        tmp.add(newWord);
                    }
                }
                wchar[i] = c;
            }
        }
        return twoEndBFS(tmp,endSet,set,level + 1);
    }
}

转载于:https://my.oschina.net/liyurong/blog/1543627

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值