CH1601 【模板】前缀统计

博客提及题目地址,并指出可直接将ed[]数组作为字符串存在数量的计数数组。

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

题目地址


注意点:

  • 直接将ed[]数组作为字符串存在数量的计数数组即可.

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int SIZE=1000010;
int trie[SIZE][26],ed[SIZE],tot=1;
void insert(char* str){
	int len=strlen(str),p=1;
	for(int i=0;i<len;i++){
		int ch=str[i]-'a';
		if(!trie[p][ch])trie[p][ch]=++tot;
		p=trie[p][ch];
	}
	ed[p]+=1;
}
int search(char* str){
	int ans=0;
	int len=strlen(str),p=1;
	for(int i=0;i<len;i++){
		int ch=str[i]-'a';
		if(!trie[p][ch])return ans;
		p=trie[p][ch],ans+=ed[p];
	}
	return ans;
}
char str[SIZE];
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%s",&str);
		insert(str);
	}
	for(int i=1;i<=m;i++){
		scanf("%s",&str);
		printf("%d\n",search(str));
	}
	return 0;
}

 

<think>我们正在讨论字典树(Trie树)的概念、性质以及使用STL实现的方式。用户的问题包括节点结构、查找数和空间优化方法。同时,用户还提到要判断选项的正确性,但当前问题中并没有提供选项,因此我们专注于解释Trie树本身。根据引用[1][2][3][4]中关于Trie树的信息:概念:Trie树(前缀树或字典树)是一种树形数据结构,用于高效存储和检索字符串集合。它的核心思想是利用字符串的公共前缀来减少查询时间,提高效率。性质:1.每个节点包含多个子节点(引用[2]提到“字典树不是二叉树,字典树的每一个节点可以有多个子节点”)。2.除根节点外,每个节点存储一个字符(通常是字符,但也可以用于其他类型的数据)。3.从根节点到某一节点的路径,表示该节点对应的字符串(引用[2]:“顺着根节点向子节点连接,可以找到一个字符串信息”)。4.具有公共前缀字符串共享祖先节点,节省空间(引用[2]:“具有公共前缀字符串不需要重复存储”)。结构:-节点结构:通常包含一个指向子节点的指针数组(或映射)和一个标记,用于表示是否为一个单词的结束。-根节点:不存储字符,作为搜索的起始点。-在引用[1]中,Trie树的结构体定义为:```gotypetriestruct{root*node//根节点指针sizeint//存放的元素数量mutexsync.Mutex//并发控制锁}```这里虽然是用Go语言描述的,但原理相同。查找操作:-从根节点开始,逐个字符检查。-若某字符对应的子节点不存在,则查找失败。-若成功到达最后一个字符对应的节点,且该节点标记为单词结束,则查找成功。-时间复杂度:O(m),其中m为待查找字符串的长度。空间优化方法:1.压缩Trie:将只有一个子节点的节点与其子节点合并,如PatriciaTrie(压缩前缀树)。2.使用动态数组或映射(map)来存储子节点,而不是固定大小的数组(例如,当字符集很大时,使用map可以避免分配大量空指针)。3.按需创建子节点:在插入时创建节点,避免预先分配所有可能子节点。STL实现方式:STL(C++标准模板库)中没有直接提供Trie树的容器。但我们可以使用标准库中的容器(如`std::map`或`std::unordered_map`)来实现Trie节点。示例节点结构:```cppstructTrieNode{boolisEnd;//标记是否是单词的结尾std::map<char,TrieNode*>children;//子节点映射//或者使用数组:TrieNode*children[26];如果只考虑小写字母};```查找数:-查找一个字符串需要遍历字符串的每个字符,每个字符的查找在子节点映射中是O(1)(如果使用数组)或O(logk)(如果使用map,k为子节点数量),所以整体为O(m)。空间优化:-使用映射(map)而不是固定大小的数组,可以节省空间(尤其是当字符集很大但实际使用的字符不多时)。-在节点中存储指向子节点的指针,只有当需要时才创建节点。现在,由于用户的问题中没有提供具体的选项,我们主要解释了Trie树的相关概念和实现。如果用户后续提供选项,我们可以进一步判断。相关问题:1.Trie树在哪些实际应用中表现优异?2.如何用C++实现一个支持Unicode字符的Trie树?3.Trie树与哈希表在字符串查找上各有什么优缺点?4.如何实现Trie树的删除操作?</think>### 字典树(Trie树)概念与实现 #### 一、核心概念与性质 1. **数据结构本质** Trie树是一种**树形数据结构**(非二叉树),用于高效存储和检索字符串集合(引用[2])。其核心特点: - 每个节点存储**一个字符**(除根节点外) - 从根节点到叶节点的路径表示**完整字符串** - 具有公共前缀字符串**共享祖先节点**(空间优化关键) 2. **关键性质** | 特性 | 说明 | 优势 | |------|------|------| | 前缀共享 | `"ace"`和`"act"`共享`"ac"`节点 | 空间复杂度$O(N·L)$ → $O(N)$优化 | | 快速检索 | 搜索时间复杂度$O(L)$($L$为字符串长度) | 比哈希表更高效的前缀匹配 | | 有序存储 | 字典序自然排列 | 支持按字典序遍历 | #### 二、节点结构与STL实现 **节点结构定义**(引用[1]): ```cpp struct TrieNode { bool isEnd; // 标记单词结束 unordered_map<char, TrieNode*> children; // 子节点映射 // 数组实现:TrieNode* children[26];(仅小写字母) }; ``` **完整Trie结构**(引用[1][4]): ```cpp class Trie { private: TrieNode* root; int size; // 存储元素数量 mutex mtx; // 线程安全控制(引用[1]) public: Trie() : root(new TrieNode()), size(0) {} // 插入操作(禁止重复) bool insert(const string& word) { lock_guard<mutex> lock(mtx); TrieNode* node = root; for (char c : word) { if (!node->children.count(c)) { node->children[c] = new TrieNode(); } node = node->children[c]; } if (node->isEnd) return false; // 重复插入失败 node->isEnd = true; size++; return true; } // 搜索操作 bool search(const string& word) { TrieNode* node = root; for (char c : word) { if (!node->children.count(c)) return false; node = node->children[c]; } return node->isEnd; } }; ``` #### 三、查找数与性能 1. **查找操作类型** - **精确查找**:`search("apple")` → 需遍历完整路径并验证`isEnd` - **前缀查找**:`startsWith("app")` → 只需验证路径存在 2. **时间复杂度** - 插入/搜索/删除:$O(L)$($L$为字符串长度) - 前缀匹配:$O(P)$($P$为前缀长度) 3. **空间优化方法** | 方法 | 实现方式 | 适用场景 | |------|----------|----------| | 压缩Trie | 合并单链路径节点 | 长字符串且公共前缀少 | | 动态子节点 | 使用`unordered_map`替代数组 | 字符集大且稀疏(如Unicode) | | 延迟加载 | 按需创建子节点 | 内存敏感场景 | | 终结标记复用 | `isEnd`兼作数据存储指针 | 需关联额外数据时 | #### 四、STL中的间接实现 STL无直接Trie容器,但可组合实现: ```cpp // 使用std::map模拟Trie map<string, int> prefixMap; // 构建前缀索引 void buildTrie(const vector<string>& words) { for (const auto& word : words) { for (int i = 1; i <= word.size(); ++i) { prefixMap[word.substr(0, i)]++; // 前缀计数 } } } // 前缀搜索 int countPrefix(const string& prefix) { return prefixMap.count(prefix) ? prefixMap[prefix] : 0; } ``` #### 五、应用场景(引用[3]) 1. **自动补全**:输入法实时建议(前缀匹配) 2. **拼写检查**:快速验证单词是否存在 3. **IP路由表**:最长前缀匹配算法 4. **词频统计**:结合`isEnd`计数 5. **最大异或对**(引用[4]):二进制位存储与检索 > Trie树的检索效率$O(L)$使其在**大规模字符串处理**中显著优于哈希表(平均$O(1)$但碰撞时$O(N)$)和二叉搜索树($O(L \log N)$),尤其擅长前缀相关操作[^2][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值