(复习)[LeetCode]Word Ladder

Question
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:

Only one letter can be changed at a time
Each intermediate word must exist in the word list
For example,

Given:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
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.


本题难度Medium。有2种算法分别是: BFS 和 Two End BFS

1、BFS

【复杂度】
时间 O(N) 空间 O(N)

【思路】
因为要求最短路径,如果我们用深度优先搜索的话必须遍历所有的路径才能确定哪个是最短的,而用广度优先搜索的话,一旦搜到目标就可以提前终止了,而且根据广度优先的性质,我们肯定是先通过较短的路径搜到目标。另外,为了避免产生环路和重复计算,我们找到一个存在于字典的新的词时,就要把它从字典中移去。这么做是因为根据广度优先,我们第一次发现词A的路径一定是从初始词到词A最短的路径,对于其他可能再经过词A的路径,我们都没有必要再计算了。(在这里,为了寻求单词之间的路径关系,会遍历单词的每个位置,对其做从'a''z'的替换)

【注意】
base case为队列q.size()==0而不是wordList.size()==0,因为并不是wordList中所有的单词都会被BFS到。

【代码】

public class Solution {
    public int ladderLength(String beginWord, String endWord, Set<String> wordList) {
        //require
        Queue<String> q=new LinkedList<>();
        q.offer(beginWord);
        //invariant
        return bfs(2,q,endWord,wordList);
    }
    private int bfs(int length,Queue<String> q,String endWord, Set<String> wordList){
        //base case
        if(q.size()==0){
            return 0;
        }

        Queue<String> nextQ=new LinkedList<>();
        while(!q.isEmpty()){
            String word=q.poll();
            char[] cArray = word.toCharArray();
            for(int i=0;i<word.length();i++){
                for(char c='a';c<='z';c++){
                    char ci=cArray[i];
                    cArray[i]=c;
                    String tmp=new String(cArray);
                    //包括word本身也要进行检查
                    if(tmp.equals(endWord))
                        return length;
                    else if(wordList.contains(tmp)){
                        nextQ.offer(tmp);
                        wordList.remove(tmp);
                    }
                    cArray[i]=ci;
                }
            }
        }
        return bfs(length+1,nextQ,endWord,wordList);
    }
}

2、Two End BFS

【复杂度】
时间 O(N) 空间 O(N)

【思路】
如果把各个单词之间的路径画出来中你会发现有个特点,它是个“纺锤体”:

这里写图片描述

通过第一个方法我们知道查找单词之间的路径的方法是遍历:

String word=q.poll();
char[] cArray = word.toCharArray();
for(int i=0;i<word.length();i++){
    for(char c='a';c<='z';c++){
        char ci=cArray[i];
        cArray[i]=c;
        String tmp=new String(cArray);
        if(tmp.equals(endWord))
            return length;
        else if(wordList.contains(tmp)){
            nextQ.offer(tmp);
            wordList.remove(tmp);
        }
        cArray[i]=ci;
    }
}

也就是说每个word都要耗费traversal time = word.length()*26次的遍历时间。traversal time是无法降低的,但是我们可以通过降低word的个数来提高性能,这也正是两端BFS名称的由来。通过产生两个集合ssls,保证ss是单词较少的那个,遍历ss中的单词cur,只要ls中有cur就返回;如果没有就遍历curwordList中的下一个单词并加入到集合ns中。结束后将lsns中小的给ss,大的给ls实际上我们可以看出在第一次迭代中ss存放的是beginWord,而第二次迭代中ss存放的是endWord(这就是两端BFS的核心所在)。

【代码】

public class Solution {
    public Set<String> getNeighbors(String cur, Set<String> dict) {
        Set<String> neighbors = new HashSet<>();
        char[] cArray = cur.toCharArray();
        for (int i = 0; i < cArray.length; ++i) {
            char ci= cArray[i];
            for (char c = 'a'; c <= 'z'; ++c) {
                if (c == ci) {
                    continue;
                }
                cArray[i] = c;
                String s = new String(cArray);
                if (dict.contains(s)) {
                    neighbors.add(s);
                }
            }
            cArray[i] = ci;
        }
        return neighbors;
    }
    public int ladderLength(String beginWord, String endWord, Set<String> wordList) {
        Set<String> s1 = new HashSet<>();
        Set<String> s2 = new HashSet<>();
        s1.add(beginWord);
        s2.add(endWord);

        // choose the set has the smaller size
        boolean isS1 = s1.size() < s2.size() ? true : false;
        Set<String> ss = isS1 ? s1 : s2;
        Set<String> ls = isS1 ? s2 : s1;
        int res = 0;
        while (!ss.isEmpty()){
            ++res;

            // remove for better performance and solve the visited problem
            wordList.removeAll(ss);

            // similar as queue.poll() in BFS
            Set<String> ns = new HashSet<>();

            for (String cur : ss) {
                if (ls.contains(cur)) {
                    return res;
                }
                for (String neighbor : getNeighbors(cur, wordList)) {
                        ns.add(neighbor);
                }
            }

            // choose the set has the smaller size
            isS1 = ns.size() < ls.size() ? true : false;
            ss = isS1 ? ns : ls;
            ls = isS1 ? ls : ns;
        }
        return 0;
    }
}

【附】

  1. 本题对于beginWord与endWord相等的情况,length 可以等于1也可以等于2
  2. 方法1是先对word进行变形再检查是否等于endWord(其中还包括word自己),是一种映射方法;方法2是先检查ss中的cur是否包含于ls,再变形cur是一种集合方法

参考

[Leetcode] Word Ladder 单词爬梯

beat 95% two end BFS java soluction

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值