LeetCode 212. 单词搜索 II(C++)*

思路:
使用回溯法进行深度遍历同时判断字符串。
1.原始的回溯法,通过限制字符串长度作为判断条件,会超时,需要优化判断方式
2.使用字典树来判断目前遍历的字符串是否满足前缀条件,如果满足就直接剪枝,提高了效率
原题链接:https://leetcode.cn/problems/word-search-ii/description/?favorite=2ckc81c
参考答案:https://leetcode.cn/problems/word-search-ii/solutions/1000172/dan-ci-sou-suo-ii-by-leetcode-solution-7494/

1.题目如下:

给定一个 m x n 二维字符网格 board 和一个单词(字符串)列表 words, 返回所有二维网格上的单词 。

单词必须按照字母顺序,通过 相邻的单元格 内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。

示例 1:

输入:board = [["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]], words = ["oath","pea","eat","rain"]
输出:["eat","oath"]

示例 2:

输入:board = [["a","b"],["c","d"]], words = ["abcb"]
输出:[]

提示:

m == board.length
n == board[i].length
1 <= m, n <= 12
board[i][j]是一个小写英文字母
1 <= words.length <= 3 * 104
1 <= words[i].length <= 10
words[i] 由小写英文字母组成
words 中的所有字符串互不相同

2.代码如下:

class Solution {
private:
    unordered_set<string> setTemp;
    vector<vector<int>> pos={{0,1},{1,0},{-1,0},{0,-1}};
    int maxl=0;
public:
//思路一:回溯法   超时
/*
    通过深度优先遍历和回溯法来遍历单词,用mark标记使用过的字符
    通过set数组来判断单词是否有效效率更高
    超时;  需要用前缀树来减少无效的遍历
*/
/*
    vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
        //set作为判断依据
        for(int i=0;i<words.size();i++){
            setTemp.insert(words[i]);
        }
        //标记防止重复遍历
        vector<vector<int>> mark(board.size(),vector<int>(board[0].size(),0));
        string temp;
        vector<string> ans;
        // 最大长度 ,可以作为退出遍历的条件
        for(int i=0;i<words.size();i++){
            maxl=maxl>=words[i].size()?maxl:words[i].size();
        }

        for(int i=0;i<board.size();i++){
            for(int j=0;j<board[0].size();j++){
                temp.push_back(board[i][j]);
                dfs(board,ans,mark,temp,1,i,j);
                temp.pop_back();
            }
        }
        return ans;
    }

    void dfs(vector<vector<char>>& board, vector<string> &ans,vector<vector<int>> mark,string temp,int dp,int i,int j){
        if(dp>maxl){
            return;
        }
        if(mark[i][j]==1){
            return;
        }
        //满足条件,放入答案
        if(setTemp.count(temp)!=0 && find(ans.begin(),ans.end(),temp)==ans.end()){
            ans.push_back(temp);
        }
        //标记为1 防止重复遍历
        mark[i][j]=1;
        for(int k=0;k<pos.size();k++){
            int newX=i+pos[k][0];
            int newY=j+pos[k][1];
            if(newX >=0 && newX<board.size() && newY>=0 && newY<board[0].size()){
                temp.push_back(board[newX][newY]);
                dfs(board,ans,mark,temp,dp+1,newX,newY);
                temp.pop_back();
            }
        }
        mark[i][j]=0;
    }
*/

//思路二:回溯法 + 字典树判断前缀优化

    //先构造字典树结构
    struct TrieNode {
    string word;
    unordered_map<char,TrieNode *> children;
    TrieNode() {
        this->word = "";
    }   
};
    void insertTrie(TrieNode* root,const string &word) {
        TrieNode*node=root;
        //将字符串数组的字符串导入到字典树中
        //有相同前缀的字符串会指向相同的map位置,但是word不同
        for (auto c:word){
            if (!node->children.count(c)) {
                node->children[c]=new TrieNode();
            }
            node=node->children[c];
        }
        node->word=word;
    }

    //用于遍历
    int dirs[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
    //深度优先遍历
    bool dfs(vector<vector<char>>& board, int x, int y, TrieNode * root, set<string>& res) {
        char ch=board[x][y]; 
        //如果目前的字符串不是words中的任何一个字符串的前缀,直接return
        if (!root->children.count(ch)) {
            return false;
        }
        //如果是前缀,则继续深度遍历
        root=root->children[ch];
        //如果root->word.size()>0,则代表遍历到此处有满足条件的字符串,所以放入答案
        if (root->word.size()>0) {
            res.insert(root->word);
        }

        //回溯法
        board[x][y]='#';
        for (int i=0;i<4;++i) {
            int nx=x+dirs[i][0];
            int ny=y+dirs[i][1];
            if (nx>=0 && nx<board.size() && ny>=0 && ny<board[0].size()) {
                if (board[nx][ny]!='#') {
                    dfs(board,nx,ny,root,res);
                }
            }
        }
        board[x][y] = ch;

        return true;      
    }

     vector<string> findWords(vector<vector<char>> & board, vector<string> & words) {
        TrieNode * root = new TrieNode();
        set<string> res;
        vector<string> ans;

        //导入字典树
        for (auto & word: words){
            insertTrie(root,word);
        }
        for (int i = 0; i<board.size(); ++i) {
            for (int j=0;j<board[0].size(); ++j) {
                dfs(board,i,j,root,res);
            }
        }        
        for (auto & word: res) {
            ans.emplace_back(word);
        }
        return ans;        
    }

};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_panbk_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值