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;
        }
    }
};

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

相关文章
|
26天前
|
算法
LeetCode第81题搜索旋转排序数组 II
文章讲解了LeetCode第81题"搜索旋转排序数组 II"的解法,通过二分查找算法并加入去重逻辑来解决在旋转且含有重复元素的数组中搜索特定值的问题。
LeetCode第81题搜索旋转排序数组 II
|
27天前
|
算法
LeetCode第74题搜索二维矩阵
文章讲解了LeetCode第74题"搜索二维矩阵"的解决方案,利用二分搜索法将问题简化,并通过数学转换找到二维矩阵中的对应元素,展示了将二维问题转化为一维问题的解题技巧。
LeetCode第74题搜索二维矩阵
|
27天前
|
算法
LeetCode第58题最后一个单词的长度
LeetCode第58题"最后一个单词的长度"的解题方法,通过从字符串末尾向前遍历并计数非空格字符,直接得出最后一个单词的长度。
LeetCode第58题最后一个单词的长度
|
27天前
|
算法
LeetCode第35题搜索插入位置
这篇文章介绍了LeetCode第35题"搜索插入位置"的解题方法,通过使用二分查找法,高效地找到在有序数组中插入一个目标数的最佳位置。
LeetCode第35题搜索插入位置
|
27天前
|
算法
LeetCode第33题搜索旋转排序数组
这篇文章介绍了LeetCode第33题"搜索旋转排序数组"的解题方法,通过使用二分查找法并根据数组的有序性质调整搜索范围,实现了时间复杂度为O(log n)的高效搜索算法。
LeetCode第33题搜索旋转排序数组
|
1月前
|
算法 JavaScript Python
【Leetcode刷题Python】79. 单词搜索和剑指 Offer 12. 矩阵中的路径
Leetcode第79题"单词搜索"的Python解决方案,使用回溯算法在给定的二维字符网格中搜索单词,判断单词是否存在于网格中。
21 4
|
1月前
|
Python
【Leetcode刷题Python】生词本单词整理
文章提供了一个Python程序,用于帮助用户整理和排版生词本上的单词,包括去除重复单词、按字典序排序,并按照特定的格式要求进行打印排版。
21 3
|
1月前
|
Python
【Leetcode刷题Python】318. 最大单词长度乘积
本文提供了LeetCode题目318的Python编程解决方案,题目要求在一个字符串数组中找出两个不含有公共字母的单词,且这两个单词的长度乘积最大,如果不存在这样的两个单词,则返回0。
12 0
|
1月前
|
算法 Python
【Leetcode刷题Python】74. 搜索二维矩阵
两种解决LeetCode "搜索二维矩阵" 问题的方法的Python实现。第一种方法是从二维矩阵的右上角开始线性搜索,通过比较当前元素与目标值来决定搜索方向。第二种方法是将二维矩阵视为一维数组进行二分查找,通过计算中间元素的行列索引来更新搜索区间。两种方法都旨在高效地判断目标值是否存在于给定的有序二维矩阵中。
19 0
|
1月前
|
算法 索引 Python
【Leetcode刷题Python】33. 搜索旋转排序数组
解决LeetCode "搜索旋转排序数组" 问题的Python实现代码。代码使用了二分查找算法,首先检查目标值是否存在于数组中,然后通过比较数组中间值与数组首尾值来确定应该在数组的哪一半继续搜索,直到找到目标值或搜索范围为空。如果找到目标值,返回其索引;如果搜索结束仍未找到,返回 -1。
12 0