LeetCode:实现单词接龙

给定一个起始字符串和一个终止字符串,以及一个单词表,求是否可以将起始字符串酶促改一个字符,
直到改成终止字符串,且所有中间的修改过程表示的字符串都可以在单词表里面找到.若存在,输出
需要修改次数最少的所有更改方式.

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

解题思路:
我们可以把起始字符串,终止字符串,以及单词表里面所有的字符串想象成节点.若两个字符串只有一个字符不同,
那么它们相连.因为题目需要输出修改次数最少的所有修改方式,因此可以使用广度优先搜索,求得起始节点到
终止节点的最短距离.
同时还使用一个小技巧:并不是直接从起始节点进行广度优先搜索,直到找到终止节点为止:而是从起始节点和
终止节点分别进行广度优先搜索,每次只延展当前层节点数最少的那一端,这样可以减少搜索的总结点数.举例来说,
假设最短距离为4,如果只从一端搜索4层,总遍历节点数最多是1+2+4+8+16=31;而如果从两端各搜索两层,总
遍历节点数最多只有2x(1+2+4)=14;
在搜索结束后,还需要通过回溯法来重建所有可能的路径.
#include <iostream>
#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <algorithm>

using namespace std;

class Solution{
public:
    vector<vector<string>> findLadders(string beginWord,string endWord,vector<string>& wordList){
        vector<vector<string>> ans;
        unordered_set<string> dict;
        for(const auto&w:wordList)
            dict.insert(w);
        if(!dict.count(endWord))/*set容器查找,返回0或者1*/
            return ans;
        dict.erase(beginWord);
        dict.erase(endWord);
        unordered_set<string> q1{beginWord},q2{endWord};
        unordered_map<string,vector<string>> next;
        bool reversed=false,found=false;
        /*广度优先遍历*/
        while(!q1.empty()){
            unordered_set<string> q;
            for(const auto&w:q1){
                string s=w;
                for(size_t i=0;i<s.size();i++){
                    char ch=s[i];
                    for(size_t j=0;j<26;j++){
                        s[i]=j+'a';
                        if(q2.count(s)){
                            reversed?next[s].push_back(w):next[w].push_back(s);
                            found=true;
                        }
                        if(dict.count(s)){
                            reversed?next[s].push_back(w):next[w].push_back(s);
                            cout<<s<<endl;
                            q.insert(s);
                        }
                    }
                    s[i]=ch;
                }
            }               
            if(found){
                break;
            } 
            for(const auto& w:q){
                dict.erase(w);
            }
            if(q.size()<=q2.size()){
                q1=q;
            }else{
                reversed=!reversed;
                q1=q2;
                q2=q;
            }
        }
        /*回溯符合条件的路径*/
        if(found){
            vector<string> path={beginWord};
            backtrack(beginWord,endWord,next,path,ans);
        }
        return ans;
    }

private:
    void backtrack(const string& src,const string& dst,unordered_map<string,
    vector<string>>&next,vector<string>&path,vector<vector<string>>& ans){
        if(src==dst){
            ans.push_back(path);
            return;
        }
        for(const auto&s:next[src]){
            path.push_back(s);
            backtrack(s,dst,next,path,ans);
            path.pop_back();
        }
    }
};

int main(int argc,char* argv[]){
    string beginWord="hit",endWord="cog";
    vector<string> wordList={"hot","dot","lot","log","cog"};
    vector<vector<string>> ans=Solution().findLadders(beginWord,endWord,wordList);
    for_each(ans.begin(),ans.end(),[](vector<string>& res){
        cout<<"[ ";
        for(const auto&i:res)
            cout<<i<<" ";
        cout<<"]";
    });
    return 0;
}

 

上一篇:Wordlist/SecLists目录作用


下一篇:【java学习】Servlet简单的表单程序(一)