Leetcode算法学习日志-127 Word Ladder

本文详细介绍了LeetCode第127题《Word Ladder》的解题思路,通过分析题目要求找到从beginWord到endWord的最短转化序列,每次只允许改动一个字符,且中间词汇必须在wordList中。解法主要运用了分支界限法结合广度优先搜索策略,用C++实现了一个高效的解决方案。
摘要由CSDN通过智能技术生成

Leetoce 127 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.

For example,

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

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.
  • You may assume no duplicates in the word list.
  • You may assume beginWord and endWord are non-empty and are not the same

题意分析

题目要求求beginword到endword的一个最短转化次数,其中中间word必须在wordList中,且endWord也在其中。每次转化只能改变一个字符,如果没有相应方法,返回0。

解法分析

此题类似于单源最短路径问题,通常采用广度优先搜索策略,由于是寻找一个最优解,所以是分支界限法的应用。分支界限法用于求解解树中的最优解,它采用广度优先搜索策略,每一个活节点只有一次机会成为可扩展节点,当一个节点成为可扩展节点时,遍历其所有儿子节点,并将活节点放入队列中,当同层可扩展节点遍历完并出队列后,开始下一层可扩展节点的遍历。如果解树带权,往往需要用优先队列来存储可扩展节点,pop出优先级最高的节点。类似于回溯法,分支界限法可以通过设定上界函数和剪枝函数来减少搜索次数。

本题中startWord和endWord分别作为起点和终点,与startword相差一个字符的字典元素作为与起点相邻的活节点点,加入可扩展队列中,每得到一个新的可扩展节点,字典中就需要删除相应元素,因为不可能有别的可扩展节点以该点为活节点了(贪心选择路径最短)。由于采用的广度优先搜索,所以在某一层达到返回条件,此时的层数(变换次数)一定是最小的。为了记录搜索层数,需要记录每一层活节点的个数,将该层活节点pop完后res++。如果可扩展节点队列没有元素了且程序没有返回,代表不存在相应变换。C++代码如下:

class Solution {
private:
    int n;
public:
    bool oneCh(string a,string b){
        int i;
        int count=0;
        for(i=0;i<n;i++){
            if(a[i]==b[i])
                count++;
        }
        return (count==n-1)?1:0;           
    }    
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        int res=2;
        deque<string> myQue;
        int sig=0;
        for(string kk:wordList){
            if(kk==endWord){
                sig=1;
                break;                
            }
        }
        if(sig==0)
            return 0;
        n=beginWord.size();
        int i,j;
        string temp;
        myQue.push_back(beginWord);
        int number;
        while(!myQue.empty()){//one cycle operate on one layer of the tree
            number=myQue.size();
            for(i=0;i<number;i++){
                temp=myQue[0];
                myQue.pop_front();       
                for(j=0;j<wordList.size();j++){
                    if(oneCh(temp,wordList[j])){
                        if(wordList[j]==endWord)
                            return res;
                        myQue.push_back(wordList[j]);
                        wordList.erase(wordList.begin()+j);
                        j--;                       
                    }                    
                }
            }
            res++;    
        }
        return 0;   
    }
};
上述方法可以用2-end BFS来做,也就是从start和end同时进行广度优先搜索,提高了效率,C++代码如下:

class Solution {
public:
    int ladderLength(string beginWord, string endWord, unordered_set<string>& wordDict) {
        unordered_set<string> head, tail, *phead, *ptail;
        head.insert(beginWord);
        tail.insert(endWord);
        int dist = 2;
        while (!head.empty() && !tail.empty()) {
            if (head.size() < tail.size()) {//如此交替,可以保证从start和end的进度相当
                phead = &head;
                ptail = &tail;
            }
            else {
                phead = &tail; 
                ptail = &head;
            }
            unordered_set<string> temp; 
            for (auto itr = phead -> begin(); itr != phead -> end(); itr++) {
                string word = *itr;
                wordDict.erase(word);
                for (int p = 0; p < (int)word.length(); p++) {
                    char letter = word[p];
                    for (int k = 0; k < 26; k++) {
                        word[p] = 'a' + k;
                        if (ptail -> find(word) != ptail -> end())
                            return dist;
                        if (wordDict.find(word) != wordDict.end()) {
                            temp.insert(word);
                            wordDict.erase(word);
                        }
                    }
                    word[p] = letter;
                }
            }
            dist++;
            swap(*phead, temp);
        }
        return 0; 
    }
};





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值