leetcode-212:单词搜索 II

简介: leetcode-212:单词搜索 II

题目

题目连接

给定一个 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"]
输出:[]

解题

方法一:每个单词分别dfs(超时)

在题目leetcode-79:单词搜索里面,直接dfs不会超时,效果和字典树差不多,但是遇到多次查询的时候,直接回溯的效果,远不如字典树

class Solution {
public:
    int m,n;
    vector<vector<int>> dirs={{-1,0},{0,-1},{1,0},{0,1}};
    bool dfs(vector<vector<char>>& board,string& word,vector<vector<bool>>& visited,int index,int x,int y){
        if(index==word.size()) return true;
        if(x<0||x>=m||y<0||y>=n||visited[x][y]) return false;
        if(word[index]!=board[x][y]) return false;
        visited[x][y]=true;
        for(vector<int>& dir:dirs){
            int nx=x+dir[0],ny=y+dir[1];
            if(dfs(board,word,visited,index+1,nx,ny)) return true;
        }
        visited[x][y]=false;
        return false;
    }
    vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
        m=board.size(),n=board[0].size();
        vector<string> res;
        for(string& word:words){
            bool flag=false;
            vector<vector<bool>> visited(m,vector<bool>(n,false));
            for(int i=0;i<m;i++){
                for(int j=0;j<n;j++){
                    if(dfs(board,word,visited,0,i,j)){
                        flag=true;
                        break;
                    }
                }
                if(flag) break;
            }
            if(flag) res.push_back(word);
        }
        return res;
    }
};

方法二:字典树+dfs回溯

思路:

1.先将要查询的单词全部插入到字典树中

2.从board的每个元素开始遍历,找到在字典树中出现的单词,直接加入到res中

class Trie{
public:
    bool isEnd;
    vector<Trie*> next;
    string str;
    Trie(){
        isEnd=false;
        next=vector<Trie*>(26,nullptr);
    }
    void insert(string& word){
        Trie* node=this;
        for(char c:word){
            if(node->next[c-'a']==nullptr) node->next[c-'a']=new Trie();
            node=node->next[c-'a'];
        }
        node->isEnd=true;
        node->str=word;//结尾直接记录单词(目的是为了遍历到End,将单词直接插入到res中)
    }
};
class Solution {
public:
    Trie* trie;
    int m,n;
    vector<string> res;
    vector<vector<int>> dirs={{-1,0},{0,-1},{1,0},{0,1}};
    vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
        trie=new Trie();
        m=board.size(),n=board[0].size();
        for(string& word:words){
            trie->insert(word);
        }
        vector<vector<bool>> visited(m,vector<bool>(n,false));
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                Trie* node=trie->next[board[i][j]-'a'];
                if(node!=nullptr){
                    visited[i][j]=true;
                    dfs(board,i,j,node,visited);
                    visited[i][j]=false;
                }
            }
        }
        return res;
    }
    void dfs(vector<vector<char>>& board,int x,int y,Trie* node,vector<vector<bool>>& visited){
        if(node->str!=""){
            res.push_back(node->str);
            node->str="";
        }
        for(vector<int>& dir:dirs){
            int nx=x+dir[0],ny=y+dir[1];
            if(nx<0||nx>=m||ny<0||ny>=n||visited[nx][ny]) continue;
            char c=board[nx][ny];
            if(node->next[c-'a']==nullptr) continue;
            visited[nx][ny]=true;
            dfs(board,nx,ny,node->next[c-'a'],visited);
            visited[nx][ny]=false;
        }
    }
};

优化一:字典树(带删除功能)+dfs回溯

使用带删除功能的字典树,可以避免多次无效遍历

对每个节点增加一个count引用计数的属性

删除字符串时候,对应引用计数-1

class Trie{
public:
    bool isEnd;
    vector<Trie*> next;
    int count;//引用计数
    string str;
    Trie(){
        isEnd=false;
        count=0;//引用计数初始化
        next=vector<Trie*>(26,nullptr);
    }
    void insert(string& word){
        Trie* node=this;
        for(char c:word){
            if(node->next[c-'a']==nullptr){
                node->next[c-'a']=new Trie();
            }
            node=node->next[c-'a'];
            node->count++;//对应节点引用计数+1
        }
        node->isEnd=true;
        node->str=word;
    }
    void del(string& word){
        Trie* node=this;
        for(char c:word){
            Trie* tmp=node->next[c-'a'];
            tmp->count--;//对应节点引用计数-1
            node=tmp;
        }
    }
};
class Solution {
public:
    Trie* trie;
    int m,n;
    vector<string> res;
    vector<vector<int>> dirs={{-1,0},{0,-1},{1,0},{0,1}};
    vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
        trie=new Trie();
        m=board.size(),n=board[0].size();
        for(string& word:words){
            trie->insert(word);
        }
        vector<vector<bool>> visited(m,vector<bool>(n,false));
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                Trie* node=trie->next[board[i][j]-'a'];
                if(node!=nullptr&&node->count>0){//不空,并且没有被删除
                    visited[i][j]=true;
                    dfs(board,i,j,node,visited);
                    visited[i][j]=false;
                }
            }
        }
        return res;
    }
    void dfs(vector<vector<char>>& board,int x,int y,Trie* node,vector<vector<bool>>& visited){
        if(node->str!=""){
            res.push_back(node->str);
            trie->del(node->str);
            node->str="";
        }
        for(vector<int>& dir:dirs){
            int nx=x+dir[0],ny=y+dir[1];
            if(nx<0||nx>=m||ny<0||ny>=n||visited[nx][ny]) continue;
            char c=board[nx][ny];
            if(node->next[c-'a']==nullptr||node->next[c-'a']->count==0) continue;//不空,并且没有被删除
            visited[nx][ny]=true;
            dfs(board,nx,ny,node->next[c-'a'],visited);
            visited[nx][ny]=false;
        }
    }
};

在引入带删除功能的字典树后,查找性能大幅提升,但是内存泄漏还没有去解决。

相关文章
|
2月前
|
算法 索引
LeetCode(搜索插入位置)
如何使用二分查找算法来解决LeetCode上的“搜索插入位置”问题,确保时间复杂度为O(log n),并提供了详细的代码实现和分析。
17 2
|
2月前
|
索引
Leetcode第三十三题(搜索旋转排序数组)
这篇文章介绍了解决LeetCode第33题“搜索旋转排序数组”的方法,该问题要求在旋转过的升序数组中找到给定目标值的索引,如果存在则返回索引,否则返回-1,文章提供了一个时间复杂度为O(logn)的二分搜索算法实现。
26 0
Leetcode第三十三题(搜索旋转排序数组)
|
2月前
【LeetCode 39】700.二叉搜索树中的搜索
【LeetCode 39】700.二叉搜索树中的搜索
21 0
|
2月前
Leetcode(最后一个单词长度)
这篇文章介绍了两种解决LeetCode第58题的方法,即计算给定字符串中最后一个单词的长度,方法包括翻转字符串和逆向遍历统计。
22 0
|
2月前
【LeetCode 20】151.反转字符串里的单词
【LeetCode 20】151.反转字符串里的单词
25 0
|
4月前
|
算法
LeetCode第81题搜索旋转排序数组 II
文章讲解了LeetCode第81题"搜索旋转排序数组 II"的解法,通过二分查找算法并加入去重逻辑来解决在旋转且含有重复元素的数组中搜索特定值的问题。
LeetCode第81题搜索旋转排序数组 II
|
4月前
|
算法
LeetCode第74题搜索二维矩阵
文章讲解了LeetCode第74题"搜索二维矩阵"的解决方案,利用二分搜索法将问题简化,并通过数学转换找到二维矩阵中的对应元素,展示了将二维问题转化为一维问题的解题技巧。
LeetCode第74题搜索二维矩阵
|
4月前
|
算法
LeetCode第58题最后一个单词的长度
LeetCode第58题"最后一个单词的长度"的解题方法,通过从字符串末尾向前遍历并计数非空格字符,直接得出最后一个单词的长度。
LeetCode第58题最后一个单词的长度
|
4月前
|
算法
LeetCode第35题搜索插入位置
这篇文章介绍了LeetCode第35题"搜索插入位置"的解题方法,通过使用二分查找法,高效地找到在有序数组中插入一个目标数的最佳位置。
LeetCode第35题搜索插入位置
|
4月前
|
算法
LeetCode第33题搜索旋转排序数组
这篇文章介绍了LeetCode第33题"搜索旋转排序数组"的解题方法,通过使用二分查找法并根据数组的有序性质调整搜索范围,实现了时间复杂度为O(log n)的高效搜索算法。
LeetCode第33题搜索旋转排序数组