zoukankan      html  css  js  c++  java
  • LeetCode212. 单词搜索 II

    这题是LeetCode79. 单词搜索的进阶版,第79题是在二维的char数组里搜索单个单词,
    这题需要在二维char数组里搜索一组单词。

    理论上只需要对遍历单词数组,逐个套用79的dfs方法即可,不过这题题目要求了需要用Trie树做优化。

    所以,我们最开始可以把单词列表里的所有单词都插入到一个Trie树中,然后对单词做搜索的时候,除了检查下一个位置的字母不越界且未使用过(题目要求
    同一个单元格内的字母不允许被重复使用)以外,还需要检查下一个位置的字母是否是当前字母在trie树的孩子。

    通过这个额外的Trie树的剪枝,可以减小搜索空间。

    有几点需要注意:
    (1)Trie树存储每个单词的时候,需要在每个单词的结尾字母打一个标记,我们用一个整数变量id来确定当前字母是第id个字母(id从0开始)的结尾。
    id就是单词在words列表中的顺序。 我们在dfs的时候,成功搜索到单词的标志就是最终的指针指向的字母的id不为-1(构造函数中对id初始赋值为-1)。

    (2)我们可能会在char数组中多次搜索到同样的单词,所以需要额外用一个哈希表unordered_set来对dfs函数搜索到的单词的id进行记录。

    (3)我们把二维char数组board记录到一个全局二维char变量g中,并用全局变量rows和cols记录二维数组的行数和列数,这样dfs函数可以少传几个参数。

    代码如下:

    class Solution {
    public:
        vector<vector<char>> g;                  //记录二维字符数组
        int rows, cols, dx[4] = {0, 1, -1, 0}, dy[4] = {1, 0, 0, -1};          //g的行数、列数、dfs的四个方向
        unordered_set<int> ids;                   //用来去重
    
        struct Node {                                 //Trie树的节点
            int id;
            Node *son[26];
            Node() {
                id = -1;                              
                for(int i = 0; i < 26; ++i) {
                    son[i] = NULL;
                }
            }
        }*root;
    
        void insert(string &word, int id) {                  //在Trie树中插入单词,并在结尾记录id
            Node *p = root;
            for(int i = 0; i < word.size(); ++i) {
                int u = word[i] - 'a';
                if(p -> son[u] == NULL) {
                    p -> son[u] = new Node();
                }
                p = p -> son[u];
            }
            p -> id = id;
        }
    
        void dfs(int x, int y, Node *p) {
            if(p -> id != -1) {                        //搜索成功一个单词,在哈希表ids中记录id
                ids.insert(p -> id);
            }
            char temp = g[x][y];                        //题目要求同一个单元格内的字母在一个单词中不允许被重复使用,所以我们先记录下这个位置的字符,然后暂时将他修改为'.',表示不可用,dfs之后再恢复现场
            g[x][y] = '.';
            for(int i = 0; i < 4; ++i) {
                int newX = x + dx[i], newY = y + dy[i];
                if(newX >= 0 && newX < rows && newY >= 0 && newY < cols                  //如果下一个位置不越界
                && g[newX][newY] != '.' && p -> son[g[newX][newY] - 'a'] != NULL) {      //且下一个位置未被使用过(g[newX][newY] != '.'),且在Trie树中下一个字母是当前字母的孩子
                    dfs(newX, newY, p -> son[g[newX][newY] - 'a']);                      //继续搜索
                }
            }
            g[x][y] = temp;                              //恢复现场,把这个位置的字母从'.'恢复到它本来的样子
        }
        vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
            vector<string> res;
            g = board;
            rows = g.size(), cols = g[0].size();
            root = new Node();
            for(int i = 0; i < words.size(); ++i) {                  //把所有单词插入到一个Trie树中
                insert(words[i], i);
            }
            for(int i = 0; i < rows; ++i) {
                for(int j = 0; j < cols; ++j) {
                    int u = g[i][j] - 'a';                         
                    if(root -> son[u] != NULL) {                   //如果当前字母是Trie树根节点的孩子,说明从当前位置搜索单词可能有戏
                        dfs(i, j, root -> son[u]);                  //开始搜索
                    }
                }
            }
            for(auto id : ids) {                                   //去重之后,我们就知道哪些单词搜索到了,把这些搜索到的单词加入到结果数组res中
                res.push_back(words[id]);
            }
            return res;
        }
    };
    
  • 相关阅读:
    基础最短路(模板 bellman_ford)
    UVA-12304 Race(递推)
    How do you add?(递推)
    Coconuts, Revisited(递推+枚举+模拟)
    UVA-10726 Coco Monkey(递推)
    UVA-10995 Educational Journey
    UVA-10339 Watching Watches
    【React】377- 实现 React 中的状态自动保存
    【JS】376- Axios 使用指南
    【Nodejs】375- 如何加快 Node.js 应用的启动速度
  • 原文地址:https://www.cnblogs.com/linrj/p/13502388.html
Copyright © 2011-2022 走看看