第四周:[leetcode] 126. Word Ladder II

题目链接:链接
题目描述:在wordList中寻找从beginWord到endWord的最短转换序列,要求每次转化只能变换一个字符。

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

Return
[
[“hit”,”hot”,”dot”,”dog”,”cog”],
[“hit”,”hot”,”lot”,”log”,”cog”]
]

解题思路:一开始以先解题目,后考虑空间、效率的目的,没有过多考虑,写了个DFS + 截枝的思路,算法实现较为简单,但效率过低,在跑list.size() > 100的测试用例时TLE,Solution1代码如下:

Solution1:
//判断是否能从pre转化为cur
bool canChange(string pre, string cur, vector<string>& sub){
    for(int i = 0; i < sub.size(); i++){
        if(cur == sub[i])return false;
    }
    //有一个字符不同即可以转化
    int count = 0;
    for(int i = 0; i < pre.length(); i++){
        if(pre[i] != cur[i])count++;
        if(count > 1)return false;
    }
    return count == 1;
}

void findLadder(string end, vector<string>& list, vector<vector<string>>& res, vector<string>& sub){
    string cur = sub.back();
    //能转换到wordEnd则加入result容器
    if(cur == end){
        if(res.empty() || res[0].size() == sub.size())
            res.push_back(sub);
        else if(res[0].size() > sub.size()){
            res.clear();
            res.push_back(sub);
        }
        return;
    }
    //如果转换列表的长度大于当前res容器存储列表的长度,则返回,实现截枝
    if(res.size() != 0 && res[0].size() <= sub.size())return;
    //递归查找
    for(int i = 0; i < list.size(); i++){
        if(canChange(cur, list[i], sub)){
            sub.push_back(list[i]);
            findLadder(end, list, res, sub);
            sub.pop_back();
        }
    }
}

vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
    vector<vector<string>> res;
    vector<string> sub;
    sub.push_back(beginWord);
    findLadder(endWord, wordList, res, sub);
    return res;
}

算法分析:
1. 编写代码时就注意到效率最低肯定在递归查找,每次递归都进行O(n)时间寻找,效率极低,可建立unordered_map映射,保存所有已搜寻过的可转换序列加入列表,先从列表查找;
2. canChange函数判断是否能从pre转化为cur,vector数据结构的遍历也为O(n1)时间;可用红黑树实现的unordered_set数据结构,查找效率为O(log(n1));
3. 算法本身性质问题,深度搜索不保证第一次搜寻的就是最短序列,广度优先搜寻所有最优解更合适。
针对以上问题,进行改进:现广度优先搜索生成树,后进行路径生成,得解!但是,在更大更大更大数量的测试下还是差在时间问题!!!再次思考数据结构的问题···通过漫长漫长的思考以及网上的学习,发现可进行以下思路进行改进:
1.运用两个unordered_set数据结构,保存生成树前一高度与当前高度的字符串,交替变换,可将已在树中的string从list中删除,保证父树不重复出现在子树节点中;如(1)a->b,b->c,(2)a->c的情况,当广度搜寻完c时,把c删除,截去情况(1);
2.判断子节点方面,逐位置改变父节点的字符进行判断,建树效率为str.length*26*log(n)(平衡二叉树查找),而Solution1对list遍历进行查找,其效率为stri.length*n;
奋战近1.5天,Solution2解题如下(176ms AC):
网上见有88ms,56msAC的方法,休息片刻,再继续学习~总而言之,编程量较大的题目思考收获挺多的~

void findPath(string now, string end, vector<string>& sub,
              unordered_map<string, vector<string>>& path, vector<vector<string>>& res){
    if(now == end){
        sub.push_back(now);
        vector<string> t = sub;
        reverse(t.begin(), t.end());
        res.push_back(t);
        sub.pop_back();
        return;
    }
    for(int i = 0; i < path[now].size(); i++){
        sub.push_back(now);
        findPath(path[now][i], end, sub, path, res);
        sub.pop_back();
    }
}

vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
    vector<vector<string>> res;
    unordered_set<string> list(wordList.begin(), wordList.end());
    unordered_map<string, vector<string>> path;
    vector<unordered_set<string>> layer(2);

    int cur = 0;
    int pre = 1;
    list.insert(beginWord);
    layer[cur].insert(beginWord);
    while(true){
        swap(cur, pre);
        layer[cur].clear();
        for(unordered_set<string>::iterator iter = layer[pre].begin(); iter != layer[pre].end(); iter++){
            list.erase(*iter);
            string t = *iter;
            for(int i = 0; i < (*iter).size(); i++){
                string word = *iter;
                int stop = word[i] - 'a';
                for(int j = (stop + 1)%26; j != stop; j = (j+1) % 26){
                    word[i] = j + 'a';
                    //layer[pre].find(word) == layer[pre].end()除去寻找过程中已保存在pre的情况
                    if(list.find(word) != list.end() && layer[pre].find(word) == layer[pre].end()){
                        layer[cur].insert(word);
                        path[word].push_back(*iter);
                    }
                }
            }
        }
        if(layer[cur].size() == 0){
            return res;
        }
        if(layer[cur].find(endWord) != layer[cur].end()){
            break;
        }
    }

    vector<string> sub;
    //从endWord到beginWord查找
    findPath(endWord, beginWord, sub, path, res);
    return res;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值