字典树(Trie查找单词再也不用担心超时了)

本文深入探讨了字典树(Trie)在单词查找中的应用,包括其结构、工作原理以及如何利用字典树高效地进行单词搜索。通过实例分析,展示了字典树在实际编程中的应用,并提供了相关代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

字典树(Trie查找单词再也不用担心超时了)
 字典树与字典很相似,当你要查一个单词是不是在字典树中,首先看单词的第一个字母是不是在字典的第一层,如果不在,说明字典树里没有该单词,如果在就在该字母的孩子节点里找是不是有单词的第二个字母,没有说明没有该单词,有的话用同样的方法继续查找.字典树不仅可以用来储存字母,也可以储存数字等其它数据。

HDU 1247

Hat’s Words

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 5977 Accepted Submission(s): 2232


Problem Description
A hat’s word is a word in the dictionary that is the concatenation of exactly two other words in the dictionary.
You are to find all the hat’s words in a dictionary.

Input
Standard input consists of a number of lowercase words, one per line, in alphabetical order. There will be no more than 50,000 words.
Only one case.

Output
Your output should contain all the hat’s words, one per line, in alphabetical order.

Sample Input
a
ahat
hat
hatword
hziee
word

Sample Output
ahat
hatword

#include<iostream>
#include<string.h>
const int N=50;
char str[50000][N],left[N],right[N];

struct Trie {
    Trie * next[26];
bool flag;//标记是否是单词的结尾
} *root;

void insert(char *str) {
    int len = strlen(str);
    Trie *s = root;
    for (int i = 0; i < len; i++)
        if (s->next[str[i] - 'a'])
            s = s->next[str[i] - 'a'];
        else {
            Trie* t = new Trie;
            memset(t, 0, sizeof (Trie));
            s->next[str[i] - 'a'] = t;
            s = t;
        }
s->flag = 1;
}

int find(char *str) {
    int len = strlen(str);
    Trie *s = root;
    for (int i = 0; i < len; i++)
        if (s->next[str[i] - 'a'])
            s = s->next[str[i] - 'a'];
        else
            return 0;
    return s->flag;
}


int main() {
    root = new Trie;
    memset(root, 0, sizeof (Trie));
int k = 0;
    while (gets(str[k]))
        insert(str[k++]);
    for (int i = 0; i < k; i++)
{
int len=strlen(str[i]);
for (int j = 1; j < len; j++)
{
strcpy(left,str[i]);
left[j]='\0';
strcpy(right,str[i]+j);
            if (find(left)&&find(right))
{
puts(str[i]);
break;
}
        }
}
  
    return 0;
} 



HDU 1251 

统计难题

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131070/65535 K (Java/Others)
Total Submission(s): 14211    Accepted Submission(s): 6137


Problem Description
Ignatius最近遇到一个难题,老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).
 

Input
输入数据的第一部分是一张单词表,每行一个单词,单词的长度不超过10,它们代表的是老师交给Ignatius统计的单词,一个空行代表单词表的结束.第二部分是一连串的提问,每行一个提问,每个提问都是一个字符串.

注意:本题只有一组测试数据,处理到文件结束.
 

Output
对于每个提问,给出以该字符串为前缀的单词的数量.
 


Sample Input
banana
band
bee
absolute
acm
 
ba
b
band
abc
 
Sample Output
2
3
1
0

代码:
#include<iostream>
#include<cstring>
using namespace std;
struct tree
{
 tree *next[26];
 int ch;
 int flag;
} *root;

void insert(char *str) {
    int len = strlen(str);
    tree *s = root;
    for (int i = 0; i < len; i++)
        if (s->next[str[i] - 'a'])
  {
   s = s->next[str[i] - 'a'];
   s->ch++;
  }
        else {
            tree* t = new tree;
   
            memset(t, 0, sizeof (tree));
   t->ch=1;
            s->next[str[i] - 'a'] = t;
            s = t;
        }
    s->flag = 1;
}
 
int find(char *str)
{
 int n=strlen(str);
 tree *t=root;
 for(int i=0;i<n;i++)
 {
  if(t->next[str[i]-'a'])
   t=t->next[str[i]-'a'];
  else
   return 0;
 }
 int count=0;
 return t->ch;
  
}
void main()
{
 char a[15];
 root=new tree;
 memset(root,0,sizeof(tree));
 while(gets(a)&&a[0]!='\0')
 {
  insert(a);
 }
 
 while(gets(a))
 {
  printf("%d\n",find(a));
  
 }
}

  
<think>好的,我现在需要解决LeetCode 212题单词搜索II的问题。用户询问了解题思路和示例代码,所以我得先回忆一下这个题目的要求和相关解法。 首先,题目是给定一个二维字符网格和一个单词列表,要求找出所有同时在网格和列表中出现的单词单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”指的是水平或垂直相邻的单元格,同一个单元格内的字母在一个单词中不允许被重复使用。比如输入一个board和words列表,输出所有能在board中找到的words中的单词。 那么,常见的解法是什么呢?记得之前学过的,对于这种多个单词的搜索问题,直接对每个单词单独使用回溯法(DFS)可能会超时,因为如果words列表很大,每个单词都遍历整个board的话,时间复杂度会很高。例如,假设words有k个单词,每个单词长度是L,board是m*n的,那么时间复杂度大概是O(k * L * m * n * 4^L),这显然在单词数量多的时候效率很低。 这时候想到使用Trie树(字典树)来优化Trie树可以将所有待查找单词构建成树结构,这样在DFS过程中,可以同时沿着Trie树的路径进行搜索,一旦发现当前路径不在Trie树中,就可以提前剪枝,减少不必要的搜索。这样可以有效减少重复的搜索路径,提高效率。 接下来,具体步骤应该包括: 1. 构建Trie树:将所有words中的单词插入到Trie结构中,每个节点保存子节点和是否为单词结尾的标志。 2. DFS遍历board:对每个单元格作为起点,进行深度优先搜索,同时在Trie树中检查当前路径是否存在。如果存在,继续搜索;如果不存在,剪枝。当遇到Trie树中的单词节点时,将该单词加入结果集,并避免重复添加(比如可以标记该节点为非单词,或者在加入后删除)。 3. 处理重复和剪枝:需要注意在找到单词后,及时从Trie树中移除,或者在节点中添加标记,防止同一单词多次被加入结果。同时,DFS过程中需要记录已访问的单元格,避免重复使用。 然后,具体实现时,需要考虑Trie的结构。比如,每个节点可能有26个子节点(对应26个字母),或者使用哈希表存储子节点。另外,DFS的递归终止条件应该是当前位置超出board边界,或者当前字符不在Trie树的下一个节点中,或者该单元格已经被访问过。 举个例子,假设board如下: [ ['o','a','a','n'], ['e','t','a','e'], ['i','h','k','r'], ['i','f','l','v'] ] words = ["oath","pea","eat","rain"] 构建Trie树后,从每个单元格开始DFS。比如,从(0,0)的'o'开始,检查Trie中是否有以'o'开头的单词。然后向四个方向搜索,寻找下一个字符。当组成一个完整的单词时,如"oath",就将其加入结果。 此外,剪枝的关键在于及时判断当前路径是否存在于Trie中。如果当前路径已经不在Trie中,就没必要继续搜索下去。例如,如果当前路径是'oa',而Trie中不存在以'oa'为前缀的单词,就可以停止继续搜索。 接下来,代码实现的大致步骤: 1. 定义TrieNode结构,包含children和is_word字段。 2. 构建Trie树,将所有单词插入。 3. 遍历board的每个单元格,作为DFS的起点。 4. 在DFS过程中,维护当前路径形成的字符串,同时根据Trie树的节点进行判断。如果当前字符对应的子节点存在,继续搜索;如果该子节点标记为单词,则将其加入结果,并取消标记(避免重复)。 5. 使用visited矩阵或者修改board临时标记为已访问,回溯时恢复。 需要注意的问题: - 如何避免同一单词多次被添加?比如,可能在board中存在多条路径形成同一个单词。题目要求返回所有出现在words中的单词,但不重复。所以需要在找到单词后,将其从Trie中删除,或者标记为已找到。 - 优化空间:Trie的构建方式,是否可以用更节省空间的结构,比如用字典代替固定大小的数组。 - 处理特殊字符?题目中说明是二维字符网格,所以都是小写字母。 现在,参考给出的引用内容。引用3提到,当深搜路径不存在于字典树时,就停止搜索。这点对应了Trie的剪枝。而代码部分可能涉及到Trie的构建和DFS的结合。 另外,引用1、2、4都是题目描述,所以关键解法在引用3中提到的使用字典树优化DFS。 综上,解题思路是使用Trie优化DFS,减少不必要的搜索,从而提高效率。具体实现步骤包括构建Trie,遍历每个单元格进行DFS,剪枝条件判断,以及处理结果的去重。</think>### LeetCode 212 单词搜索 II 解题思路 #### 核心思路:Trie树 + DFS回溯 1. **构建Trie树** 将所有待查找单词存入字典树,便于快速判断当前路径是否为某个单词的前缀或完整单词Trie节点结构如下: - `children`: 子节点(数组或哈希表) - `is_end`: 标记是否为单词结尾 2. **DFS回溯搜索** 遍历网格中的每个单元格作为起点,通过DFS尝试所有可能的路径: - 若当前路径不在Trie树中,直接剪枝。 - 若路径形成完整单词,加入结果集并更新Trie节点(避免重复记录)。 3. **剪枝优化** - 移除已找到的单词(例如修改`is_end`为`False`)[^3]。 - 使用临时标记避免重复访问单元格。 #### 示例代码 ```python class TrieNode: def __init__(self): self.children = {} self.is_end = False class Solution: def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: # 构建Trie树 root = TrieNode() for word in words: node = root for char in word: if char not in node.children: node.children[char] = TrieNode() node = node.children[char] node.is_end = True result = [] m, n = len(board), len(board[0]) def dfs(i, j, node, path): char = board[i][j] if char not in node.children: return current_node = node.children[char] # 检查是否找到单词 if current_node.is_end: result.append(path + char) current_node.is_end = False # 避免重复 # 临时标记已访问 board[i][j] = '#' for dx, dy in [(-1,0),(1,0),(0,-1),(0,1)]: x, y = i + dx, j + dy if 0 <= x < m and 0 <= y < n and board[x][y] != '#': dfs(x, y, current_node, path + char) board[i][j] = char # 回溯 # 遍历所有起点 for i in range(m): for j in range(n): dfs(i, j, root, "") return result ``` #### 代码说明 1. **Trie树构建**:将所有单词插入字典树,标记每个单词的结束节点。 2. **DFS函数**:从`(i,j)`出发,沿四个方向搜索: - 若字符不在Trie树中则终止。 - 若找到完整单词,加入结果并标记为已找到。 - 临时修改`board[i][j]`为`#`防止重复访问,回溯时恢复。 3. **时间复杂度**:$O(m \times n \times 4^L)$,其中$L$为最长单词长度,但通过Trie剪枝大幅减少实际计算量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值