首页 > 其他分享 >【9.8】树结构-使用字典树解单词搜索 II

【9.8】树结构-使用字典树解单词搜索 II

时间:2025-01-23 11:28:50浏览次数:3  
标签:node word 树结构 resultSet II 树解 board words 字符串

一、题目

        给定一个 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 中的所有字符串互不相同

二、解题思路

        除了回溯算法,我们还可以利用字典树来解决这个问题。具体的解题思路如下:首先,我们根据给定的字符串列表 `words` 构建一个字典树。接着,我们从二维数组中的每个字符出发,尝试在字典树中搜索是否存在完整的字符串。为了更直观地理解这个过程,我们可以通过示例一来绘制字典树的结构图。

        题目中明确指出,字符串仅由小写英文字母组成。由于小写英文字母总共有 26 个,因此我们可以将字典树视为一棵 26 叉树,其中每个节点最多可能包含 26 个子节点。

        在构建字典树时,每个节点都需要一个标识,用于表示从根节点到当前节点的路径是否构成一个完整的字符串。这个标识的具体形式可以根据需求灵活定义。为了方便后续的计算,这里我们采用一个字符串作为标识:如果该字符串为空,则表示从根节点到当前节点的路径不是一个完整的字符串;如果该字符串非空,则表示这条路径构成了一个完整的字符串。

三、代码实现

#include <iostream>
#include <vector>
#include <unordered_set>
#include <string>

using namespace std;

// TrieNode 结构体定义
struct TrieNode {
    TrieNode* children[26];
    string word; // 用于标记从根节点到当前节点是否构成一个完整的单词
    TrieNode() {
        for (int i = 0; i < 26; ++i) {
            children[i] = nullptr;
        }
        word = "";
    }
};

// Trie 类定义
class Trie {
public:
    TrieNode* root;

    Trie() {
        root = new TrieNode();
    }

    // 插入单词到字典树
    void insert(const string& word) {
        TrieNode* node = root;
        for (char ch : word) {
            int index = ch - 'a';
            if (node->children[index] == nullptr) {
                node->children[index] = new TrieNode();
            }
            node = node->children[index];
        }
        node->word = word; // 标记完整单词
    }
};

class Solution {
public:
    vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
        Trie trie;
        // 构建字典树
        for (const string& word : words) {
            trie.insert(word);
        }

        int wordLength = words.size();
        unordered_set<string> resultSet;

        // 遍历二维网格中的每个字符
        for (int i = 0; i < board.size(); ++i) {
            for (int j = 0; j < board[0].size(); ++j) {
                dfs(board, trie.root, i, j, resultSet);
                // 如果找到所有单词,直接返回
                if (resultSet.size() == wordLength) {
                    return vector<string>(resultSet.begin(), resultSet.end());
                }
            }
        }

        return vector<string>(resultSet.begin(), resultSet.end());
    }

private:
    void dfs(vector<vector<char>>& board, TrieNode* node, int i, int j, unordered_set<string>& resultSet) {
        // 边界检查
        if (i < 0 || i >= board.size() || j < 0 || j >= board[0].size()) {
            return;
        }

        // 如果当前字符已经被访问过,直接返回
        if (board[i][j] == '#') {
            return;
        }

        // 检查字典树中是否存在当前字符
        int index = board[i][j] - 'a';
        if (node == nullptr || node->children[index] == nullptr) {
            return;
        }

        node = node->children[index];
        // 如果当前节点是一个完整单词,加入结果集
        if (!node->word.empty()) {
            resultSet.insert(node->word);
        }

        // 标记当前字符为已访问
        char tmp = board[i][j];
        board[i][j] = '#';

        // 向四个方向递归搜索
        dfs(board, node, i - 1, j, resultSet); // 上
        dfs(board, node, i + 1, j, resultSet); // 下
        dfs(board, node, i, j - 1, resultSet); // 左
        dfs(board, node, i, j + 1, resultSet); // 右

        // 恢复当前字符
        board[i][j] = tmp;
    }
};

// 主函数
int main() {
    Solution solution;
    vector<vector<char>> board = {
        {'o', 'a', 'a', 'n'},
        {'e', 't', 'a', 'e'},
        {'i', 'h', 'k', 'r'},
        {'i', 'f', 'l', 'v'}
    };
    vector<string> words = {"oath", "pea", "eat", "rain"};

    vector<string> result = solution.findWords(board, words);

    // 输出结果
    cout << "Found words: ";
    for (const string& word : result) {
        cout << word << " ";
    }
    cout << endl;

    return 0;
}

        在最坏情况下,我们需要以矩阵中的每个位置作为起点进行搜索,因此这一部分的时间复杂度为 **O(M * N)**,其中 **M** 是矩阵的行数,**N** 是矩阵的列数。对于每个起点,DFS 会向四个方向递归搜索,但由于不能往回走(即不能重复访问已经访问过的节点),实际每次递归只有 **3 个有效方向**。因此,DFS 的时间复杂度为 **O(3^L)**,其中 **L** 是字符串列表 `words` 中最长字符串的长度。综合起来,算法的总时间复杂度为 **O(M * N * 3^L)**。

标签:node,word,树结构,resultSet,II,树解,board,words,字符串
From: https://blog.csdn.net/linshantang/article/details/145257047

相关文章

  • 零数组变换II
    思路对一段区间加减并且进行多次操作可以联想到差分算法,并且queries数组比较大,可以使用二分查找加快效率。这里的代码个人觉得lambda表达式更加简洁代码intminZeroArray(vector<int>&nums,vector<vector<int>>&queries){intn=nums.size(),q=queries.size();......
  • 【leetcode 22】541. 反转字符串II
    思路:其实在遍历字符串的过程中,只要让i+=(2*k),i每次移动2*k就可以了,然后判断是否需要有反转的区间。因为要找的也就是每2*k区间的起点,这样写,程序会高效很多。classSolution{publicStringreverseStr(Strings,intk){char[]ch=s.toCh......
  • Go语言【Gin框架】:JSON、AsciiJSON、PureJSON和SecureJSON的区别
    在Go语言中,JSON、AsciiJSON、PureJSON和SecureJSON是Gin框架用于发送JSON响应的方法。1.c.JSON功能:将提供的数据序列化为标准的JSON格式,并将其作为HTTP响应发送给客户端。特点:支持Unicode字符,无需将非ASCII字符转义。某些字符(如<、>和&)会被自动转义为相应的Unicode......
  • 代码随想录——动态规划31打家劫舍III(树状DP)
    这道题目是打家劫舍III(HouseRobberIII),是打家劫舍系列问题的变种。问题描述如下:小偷发现了一个新的区域,这个区域的所有房屋排列类似于一棵二叉树。如果两个直接相连的房屋在同一晚被打劫,房屋会自动报警。给定这棵二叉树的根节点root,求在不触发警报的情况下,小偷能够盗取的最......
  • 如何在IIS中修改网站发布首页
    在使用IIS(InternetInformationServices)发布网站时,修改首页文件是一个常见的需求。以下是详细的修改步骤和注意事项:登录IIS管理器:打开IIS管理器。您可以通过在“开始”菜单中搜索“IIS管理器”来找到它。在IIS管理器中,展开服务器节点,找到并选择您要修改的网站。找到网......
  • 如何在IIS中配置https重定向到http?
    问题描述:如何在IIS架构的服务器中配置https重定向到http?答案:将以下代码另存为web.config文件后保存到网站根目录即可生效。<?xmlversion="1.0"encoding="UTF-8"?><configuration><system.webServer><rewrite><rules>......
  • 【9.7】树结构-实现 Trie (前缀树)
    一、题目Trie(发音类似"try")或者说前缀树是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补全和拼写检查。请你实现Trie类:Trie()初始化前缀树对象。voidinsert(Stringword)向前缀树中插入字符串word。boolean......
  • 【9.1】树结构-从先序遍历还原二叉树
    一、题目        我们从二叉树的根节点root 开始进行深度优先搜索。        在遍历中的每个节点处,我们输出 D 条短划线(其中 D 是该节点的深度),然后输出该节点的值。(如果节点的深度为D,则其直接子节点的深度为D+1。根节点的深度为0)。       ......
  • LeetCode:122.买卖股票的最佳时机II
    LeetCode:122.买卖股票的最佳时机IImathtcg4d..解题思路前提:上帝视角,知道未来的价格。局部最优:见好就收,见差就不动,不做任何长远打算。解题步骤新建一个变量,用来统计总利润。遍历价格数组,如果当前价格比昨天高,就在昨天买,今天卖,否则就不交易。遍历结束后,返回所有利润之和。/**......
  • 以太网详解(五)GMII、RGMII、SGMII接口时序约束(Quartus 平台)
    文章目录接口时序AvalonStreaming接口时序ReceiveTimingTransmitTimingGMII接口时序ReceiveTimingTransmitTimingRGMII接口时序ReceiveTimingTransmitTiming如何创建.sdc约束文件三速以太网系统时钟信号创建set_input_delay,set_output_delay约束set_in......