127.Word Ladder (C++实现)

0.题目描述

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 transformed word must exist in the word list. Note that beginWord is not a transformed word.

示例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.
示例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.
简要概述

给定一个其实单词和一个终止单词,从起始单词出发,每次在所给单词列表中找一个单词有且只有一个字母与当前单词不同,如果能到达终止单词,返回最短路径的长度。

1.解题思路

此题目与https://blog.csdn.net/kaotuduyazhuo4307/article/details/82257716中所述最少完全平方数思路形同。通过图的广度优先查找,得到最短路径,本文从直观思路出发,一步一步优化,最终给出一个相对优化的解。

class Solution {
public:
    bool isTransformable(string s1, string s2){
        int diffCount = 0;
        for(int i=0; i < s1.length(); i ++){
            if(s1[i] != s2[i])
                diffCount ++;
        }
        return diffCount == 1;
    }
    //version 1.0
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        //首先判断wordList是否包含endWord,否则返回0
        vector<bool> visited(wordList.size(), false);
        bool has = false;
        for(string s : wordList){
            if(s==endWord){
                has = true;
                break;
            }
        }
        if(not has)
            return 0;

        queue<pair<string, int> > queue1;
        queue1.push(make_pair(beginWord, 1));
        while(not queue1.empty()){
            string curNode = queue1.front().first;
            int step = queue1.front().second;
            queue1.pop();
            if(isTransformable(endWord, curNode)){
                return step + 1;
            }

            for(int i=0; i < wordList.size(); i++){
                if(not visited[i]){
                    if(isTransformable(wordList[i], curNode)){
                        queue1.push(make_pair(wordList[i], step + 1));
                        visited[i] = true;
                    }
                }
            }
        }
        return 0;
    }
    };
    //优化查找
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        //首先判断wordList是否包含endWord,否则返回0
        unordered_set<string> toVisit(wordList.begin(), wordList.end());
        if(toVisit.find(endWord) == toVisit.end())
            return 0;

        queue<pair<string, int> > queue1;
        queue1.push(make_pair(beginWord, 1));
        while(not queue1.empty()){
            string curNode = queue1.front().first;
            int step = queue1.front().second;
            queue1.pop();
            if(isTransformable(endWord, curNode)){
                return step + 1;
            }

            for (auto s = toVisit.begin(); s!=toVisit.end(); ){
                if(isTransformable(*s, curNode)){
                    queue1.push(make_pair(*s, step + 1));
                    s = toVisit.erase(s);
                } else
                    s++;
            }
        }
        return 0;
    }
注意:(关于C++中set、unordered_set的使用)

     如果使用range-based for循环形式遍历set这种容器,使用.erase()将前途未卜,如上所示,用auto s去遍历toVisit这个无需集合,如果不加处理的使用toVisit.erase(s)将引发致命错误。

     原因在于对s所指元素调用erase(),会使得s不在是toVisited的一个有效迭代器,此后未对s进行重新赋值就径直使用s会导致不明确行为。解决方法如上所示,.erase()总是返回一个迭代起指向其后继的元素,于是分别对待erase()和不需要erase()的两种情况即可。

    //但是如果不需要返回查找路径,完全不需要存储太多中间变量,pair还是挺费时间和空间的;此外如果从两头向中间找,会节省很多时间
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        //首先判断wordList是否包含endWord,否则返回0
        unordered_set<string> wordSet(wordList.begin(), wordList.end());
        unordered_set<string> beginNodes{beginWord};
        unordered_set<string> endNodes{endWord};
        unordered_set<string> *thisPointer = &beginNodes, *thatPointer = &endNodes;
        unordered_set<string> nextNodes;
        int step = 1;
        if(wordSet.erase(endWord) == 0) // 如果list中不存在endWord,返回0;
            return 0;
        while(not thisPointer->empty() and not thatPointer->empty()){
            if(thisPointer->size() > thatPointer->size())
                swap(thisPointer, thatPointer);


            for(auto curString=thisPointer->begin(); curString!=thisPointer->end(); curString++){
                //遍历所有的当前节点

                for(auto s=thatPointer->begin(); s!=thatPointer->end(); s++){
                    if(isTransformable(*curString, *s))
                        return step + 1;
                }


                for(auto s=wordSet.begin(); s!=wordSet.end();){
                    if(isTransformable(*curString, *s)){
                        nextNodes.insert(*s);
                        s=wordSet.erase(s);
                    }else
                        s ++;
                }
            }
            *thisPointer = nextNodes;
            nextNodes.clear();

            step++;
        }
        return 0;
    }
    //进一步优化,更换对比方法
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        //首先判断wordList是否包含endWord,否则返回0
        unsigned long stringSize = beginWord.size();
        unordered_set<string> wordSet(wordList.begin(), wordList.end());
        unordered_set<string> beginNodes{beginWord}, endNodes{endWord}, nextNodes;
        unordered_set<string> *thisPointer = &beginNodes, *thatPointer = &endNodes;
        int step = 1;
        if(wordSet.erase(endWord) == 0) // 如果list中不存在endWord,返回0;
            return 0;
        while(not thisPointer->empty()){
            if(thisPointer->size() > thatPointer->size())
                swap(thisPointer, thatPointer);
            for (const auto &curString : *thisPointer) {
                //遍历所有的当前节点
                for(int i=0; i< stringSize; i++){
                    string nextNode = curString;
                    for(char c='a'; c<'z'; c++){
                        nextNode[i] = c;
                        if(thatPointer->find(nextNode)!=thatPointer->end())
                            return step + 1;
                        if(wordSet.find(nextNode) != wordSet.end()){
                            wordSet.erase(nextNode);
                            nextNodes.insert(nextNode);
                        }
                    }
                }
            }
            *thisPointer = nextNodes;
            nextNodes.clear();
            step++;
        }
        return 0;
    }

    //去掉不必要的指针
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        //首先判断wordList是否包含endWord,否则返回0
        int stringSize = beginWord.size();
        unordered_set<string> wordSet(wordList.begin(), wordList.end());
        unordered_set<string> beginNodes{beginWord}, endNodes{endWord}, nextNodes;
        int step = 1;
        if(wordSet.erase(endWord) == 0) // 如果list中不存在endWord,返回0;
            return 0;
        while(not beginNodes.empty()){
            if(beginNodes.size() > endNodes.size())
                beginNodes.swap(endNodes);
            for (const auto &curString : beginNodes) {
                //遍历所有的当前节点
                for(int i=0; i< stringSize; i++){
                    string nextNode = curString;
                    for(char c='a'; c<'z'; c++){
                        nextNode[i] = c;
                        if(endNodes.find(nextNode)!=endNodes.end())
                            return step + 1;
                        if(wordSet.find(nextNode) != wordSet.end()){
                            wordSet.erase(nextNode);
                            nextNodes.insert(nextNode);
                        }
                    }
                }
            }
            beginNodes = nextNodes;
            nextNodes.clear();
            step++;
        }
        return 0;
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值