[LeetCode] (medium) 127. Word Ladder

https://leetcode.com/problems/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.

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.

Example 1:

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

Output: 5

Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.

Example 2:

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

Output: 0

Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.

我最开始用的最普通的广搜,使用map实现每个串和他的位置的映射,同时记录到达每一个串的步数,用于判断是否成环以及用于记录步数递增

class Solution {
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        static int fast_io = []() { std::ios::sync_with_stdio(false); cin.tie(nullptr); return 0; }();
        unordered_map<string, int> M;
        vector<int> step(wordList.size()+1, -1);
        queue<string> Q;
        string cur, tem;
        
        for(int i = 0; i < wordList.size(); ++i){
            M[wordList[i]] = i;
        }
        
        if(M.find(endWord) == M.end()) return 0;
        
        if(M.find(beginWord) == M.end()){
            M[beginWord] = wordList.size();
            step[wordList.size()] = 1;
        }else{
            step[M[beginWord]] = 1;
        }
        
        Q.push(beginWord);
        
        while(!Q.empty()){
            cur = Q.front();    Q.pop();
            for(int i = 0; i < cur.size(); ++i){
                for(char c = 'a'; c <= 'z'; ++c){
                    tem = cur;
                    tem[i] = c;
                    if(tem == endWord) return step[M[cur]]+1;
                    if(M.find(tem) != M.end() && step[M[tem]] == -1){
                        step[M[tem]] = step[M[cur]]+1;
                        Q.push(tem);
                    }
                }
            }
        }
        
        return 0;
        
    }
};

 但是其中有很多冗余,比如成环的情况可以通过每一次直接从wordlist中删除单词来避免,步数的记录可以通过每次将同一个level的单词从队列中除去后累加int量来实现,可以减少很多map查询的操作

更好的优化是使用两个集合,从前后两端开始搜索(因为beginword和endword从本质上是平等的)。将两者中较短的那个作为当前的活跃集合,因为活跃集合中的每个串的每个位置会被替换成26个字符,并有大量的set.find操作,因此使用较短的那个来执行可以节省很多操作。(直观上这个ladder的汇聚也是梭形的)

class Solution {
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        static int fast_io = []() { std::ios::sync_with_stdio(false); cin.tie(nullptr); return 0; }();
        unordered_set<string> words(wordList.begin(), wordList.end());
        unordered_set<string> forward, backward, nextlevel;
        
        if(words.find(endWord) == words.end()) return 0;
        forward.insert(beginWord);
        backward.insert(endWord);
        int step = 2;
        
        while(!forward.empty()){
            for(auto cur : forward){
                string tem = cur;
                for(int i = 0; i < cur.size(); ++i){
                    for(int j = 0; j < 26; ++j){
                        tem[i] = 'a'+j;
                        if(backward.find(tem) != backward.end()) return step;
                        unordered_set<string>::iterator itr = words.find(tem);
                        if(itr != words.end()){
                            nextlevel.insert(tem);
                            words.erase(itr);
                        }
                    }
                    tem[i] = cur[i];
                }
            }
            if(nextlevel.size() <= backward.size()){
                forward = nextlevel;
                nextlevel.clear();
            }else{
                forward = backward;
                backward = nextlevel;
                nextlevel.clear();
            }
            ++step;
        }
        return 0;
    }
};

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值