从word ladder题目看DFS和BFS(下)

同理,我们来看下wordladder II,这个题难度更大,先来看看题目描述,

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

 

Only one letter can be changed at a time

Each transformed word must exist in theword list. Note that beginWord is not a transformed word.

Note:

 

Return an empty list if there is no suchtransformation sequence.

All words have the same length.

All words contain only lowercase alphabeticcharacters.

You may assume no duplicates in the wordlist.

You may assume beginWord and endWord arenon-empty and are not the same.

可见,题目是要输出所有的最短路径。首先,超时的DFS算法,也是因为递归太深的原因,时间复杂度是指数级别的。

classSolution {
public:
    vector<vector<string>>findLadders(string beginWord, string endWord, vector<string>&wordList)
    {
        int temp = wordList.size();
              unordered_set<string>word(wordList.begin(), wordList.end());
              word.erase(beginWord);
              int max_i = wordList.size() + 3;//路径长度最多为把所有的单词遍历一遍
              vector<string> tmp;
              vector<vector<string>>res;
              tmp.push_back(beginWord);
              helper(beginWord, endWord, word,max_i, 1,tmp,res);
             //cout << max_i <<endl;
        for(auto it=res.begin();it!=res.end();)//此处注意vector的erase需要保护现场
        {
            if((*it).size()!=max_i)
                res.erase(it);
            else it++;
        }
              return res;
    }
    void helper(string beginWord, stringendWord, unordered_set<string> &word, int &max_i, intcur_i,vector<string> tmp,vector<vector<string>> &res)
{

       if (beginWord == endWord) //找到路径,递归出口
       {
              if (cur_i < max_i)
                     max_i = cur_i;
              res.push_back(tmp);
             return;
       }
       if (word.size() == 0) //没找到路径,但是字典被删除空了,结束
       {
              return;
       }
       for (int j = 0; j < beginWord.size();j++)
       {
              for (char i = 'a'; i <= 'z';i++)
              {
                     if(beginWord[j] == i) continue;
                     char temp = beginWord[j];
                    beginWord[j] = i;
                     if (word.find(beginWord) !=word.end())
                     {
                            tmp.push_back(beginWord);
                            word.erase(beginWord);       //已经访问的单词不再遍历       
                            helper(beginWord,endWord, word, max_i, cur_i + 1,tmp,res);
                            word.insert(beginWord);    //恢复现场
                            tmp.pop_back();   //恢复现场
                     }
                    beginWord[j] = temp;
              }
       }
}
};

下面列出最终的BFS+DFS算法代码,BFS用于寻找最短路径,DFS用于回溯求出最短路径向量。中间用到了队列和unordered_set,还有unordered_map 来记录每一个单词的前驱单词数组,一个单词的前驱单词不止一个,这也就是最优路径不止一条的原因,对应于图论中的一个节点的入度大于1。问题在于我们每找到一个单词,就会把它从字典中删除,这样带来新问题,也就是这样我们最多只能记录一条最优路径,这个时候我们需要再开动下脑筋,如何找到一个节点的所有前驱节点,答案在于当我们找到一个节点的前驱结点时,我们遍历这个前驱结点所在的层,看这层是否还有满足条件的前驱节点,将其全部放入自己的前驱节点数组中去,这样我们就能找到所有的最优路径。

 

代码如下:

class Solution {
public:
   vector<vector<string>> findLadders(string beginWord, stringendWord, vector<string>& wordList)    {
      unordered_set<string>word(wordList.begin(), wordList.end());
       unordered_set<string>copyword(word);
       intn = beginWord.size();
       vector<vector<string>>res;
       vector<string>tmp;
       queue<string>q;
       q.push(beginWord);
       q.push("1");
       boolexist = false;
       intstep = 1;
       unordered_map<string,vector<string>> mp;
       while(!q.empty())
       {
              stringtemp = q.front();
              q.pop();
              if(temp == endWord)
              {
                    exist= true;
                     break;
              }
              if(temp == "1")  //遇到一层结束,则加一
              {
                     step++;
                     continue;
              }
              stringt = temp;
              for(int i = 0; i<n; i++)
              {    
                     charx = t[i];
                     for (char j = 'a'; j <= 'z'; j++)
                     {            
                            if(t[i] != j) t[i] = j;
                            if(word.find(t) != word.end())

                            {
                                   word.erase(t);
                                   q.push(t);
                                   queue<string>cop(q); //找出前驱结点层的所有满足条件的前驱节点
                                   mp[t].push_back(temp);//将其放入前驱结点数组中去
                                   stringtmp = cop.front();
                                   while(tmp != "1")
                                   {
                                          cop.pop();
                                          if(difference(t, tmp) == 1)
                                                 mp[t].push_back(tmp);//将其放入该节点的前驱结点数组中去
                                          tmp= cop.front();
                                   }
                            }
                            t[i]= x;
                     }
              }
              if(!q.empty() && q.front() == "1")
                     q.push("1");
       }
       if(exist)
       {
              tmp.push_back(endWord);
              helper(tmp,res, mp, endWord, beginWord);
       }
       returnres;
}
//比较两个字符串的差异
   int difference(string s1, string s2)
   {
       intn1 = s1.size();
       intn2 = s2.size();
       intres = 0;
       if(n1 != n2) return -1;
       for(int i = 0; i < n1; i++)
       {
              if(s1[i] != s2[i]) res++;
       }
       returnres;
}

//dfs回溯求所有的最优路径
   void helper(vector<string> tmp, vector<vector<string>>&res, unordered_map<string, vector<string>> mp,string endWord,string beginWord)
    {
       if(mp[endWord][0]==beginWord) //回溯到初始节点,递归出口
       {
           tmp.push_back(beginWord);
           reverse(tmp.begin(),tmp.end()); //需要将路径反转,因为是反着添加路径节点的
           res.push_back(tmp); //满足条件的最优路径加入到结果数组中去
           return;
       }
       for(auto i:mp[endWord]) //从终节点开始,回溯求出所有的最优路径
       {
           tmp.push_back(i);
           helper(tmp,res,mp,i,beginWord);
           tmp.pop_back();
       }        
    }
};


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值