leetcode-Word Ladder II

题目:
Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformation sequence(s) from beginWord to endWord, such that:

1.Only one letter can be changed at a time
2.Each intermediate word must exist in the word list

For example,

Given:

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

Return
[

["hit","hot","dot","dog","cog"],
["hit","hot","lot","log","cog"]

]

分析:
第一感觉是dfs + backtracking,就是先用bfs求出beginWord到endWord的最短距离,然后dfs + backtracking找到不同的路径,等于这个最短距离的留下,大于的pass,果断TLE。
简单思考了下,除了可能出现环的情况会TLE外,还可以通过剪枝的方法优化——用HashMap记录每个单词和beginWord之间的距离,当dfs遍历到某个单词已经比最短距离长的时候,就不用再遍历下去了。提交,又TLE。
仔细又一想,这种剪枝等于没剪枝,想想HashMap是怎么求出来的?是在BFS的时候层序更新的,搜索到endWord的时候就停止了。所以道理上不存在比到endWord更长的一条路。问题不是出在走了更长的路,而是因为所有的路线都是从beginWword辐射出去的,dfs搜索的话有太多的岔路。
所以一个比较自然的想法是,从endWord往回找,就可以避免许多岔路。进一步的剪枝是依据HashMap里记录的距离,规定dfs的方向只往里beginWord距离更小的方向遍历。
code:

import java.io.*;
import java.util.*;

public class WordLadderII {
    public List<List<String>> findLadders(String beginWord, String endWord, Set<String> wordDict){
        List<List<String>> Ladders = new ArrayList<List<String>>();
        //优化一:BFS时记录每个单词的前驱
        HashMap<String, List<String>> preGraph = new HashMap<String, List<String>>();
        //优化二:记录每个节点的最短距离,往回找的时候可以进一步剪枝,同时在构图的过程中也避免了环的存在
        HashMap<String, Integer> distance = new HashMap<String, Integer>();
        
        wordDict.add(beginWord);
        wordDict.add(endWord);
        for(String word : wordDict){
            preGraph.put(word, new ArrayList<String>());
        }
        
        //build the graph, get the preGraph and distance
        bfs(preGraph, distance, beginWord, wordDict);
        //find all the shortest path
        //List<String> path = new ArrayList<String>();
        dfs(Ladders, new ArrayList<String>(), endWord, beginWord, distance, preGraph);
        return Ladders;
    }
    
    /*
     *寻找差异为一的单词有两种方法:
     *(1)将目标单词与字典里的所有单词比较,O(n*l), n是字典容量(1000+),l是单词长度;
     *(2)将目标的字母用'a'-'z'挨个替换,利用Set.contains()函数搜索。由于Set.contains()是O(1)的,这种方法是O(26*l) 
     */
    public List<String> getNextWords(String words, Set<String> wordDict){
        List<String> nextWords = new ArrayList<String>();
        for(int i = 0; i < words.length(); i++){
            for(char c = 'a'; c <= 'z'; c++){
                if(c == words.charAt(i)) continue; //如果没有distance, 需要用这一步避免成环
                char[] arr = words.toCharArray();
                arr[i] = c;
                String next = new String(arr);
                if(wordDict.contains(next)) nextWords.add(next);
            }
        }
        return nextWords;
    }
    
    public void bfs(HashMap<String, List<String>> preGraph, HashMap<String, Integer> distance, String beginWord, Set<String> wordDict){
        LinkedList<String> queue = new LinkedList<String>();
        queue.offer(beginWord);
        distance.put(beginWord, 0);
        
        while(!queue.isEmpty()){
            String curr = queue.poll();
            for(String next : getNextWords(curr, wordDict)){
                preGraph.get(next).add(curr);
                if(!distance.containsKey(next)){
                    distance.put(next, distance.get(curr) + 1);
                    //保证每个单词之进入队列一次
                    queue.offer(next);
                }
            }
        }
    }
    
    public void dfs(List<List<String>> Ladders, List<String> path, String curr, String beginWord, HashMap<String, Integer> distance, HashMap<String, List<String>> preGraph){
        if(curr.equals(beginWord)){
            path.add(curr);
            Collections.reverse(path);
            Ladders.add(path);
            //Collections.reverse(path)
            //path.remove(path.size() - 1)
            return;
        }
        
        for(String next : preGraph.get(curr)){
            //剪枝,去掉非最短路的情况
            if(distance.get(curr) - 1 == distance.get(next)){
                path.add(curr);
                //如果这里直接用path而不是new ArrayList<String>(path), 68行的代码就需要执行了
                dfs(Ladders, new ArrayList<String>(path), next, beginWord, distance, preGraph);
                path.remove(path.size() - 1);
            }
        }
    }
    
    public static void main(String[] args){
        Set<String> dict = new HashSet<String>();
        dict.add("hot");
        dict.add("dot");
        dict.add("dog");
        dict.add("lot");
        dict.add("log");
        WordLadderII WL = new WordLadderII();
        List<List<String>> ladders = WL.findLadders("hit","cog",dict);
        for(List<String> list : ladders){
            System.out.println(list);
        }
    }
    
}

时间复杂度方面:
(1)bfs构图的过程中,每个点进入队列一次,时间是O(n);
(2)dfs找路线的时候最坏情况是O(n^2),平均时间复杂度是O(n).

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值