【洛谷刷题笔记】P3879 [TJOI2010] 阅读理解

这篇博客主要介绍了如何使用Trie数据结构来解决字符串查找的问题。文章首先讲解了10pts版本的基本思路,即通过建立字典树模板来存储和查找单词。然后逐步提升难度,讨论了90pts版本中因空间限制导致的错误和解决方案,最后在100pts版本中引入了bitset以节省空间并成功通过所有测试。博客作者分享了自己的思考过程,并展示了AC代码。

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

题目传送门https://www.luogu.com.cn/problem/P3879

10 pts

思路

妥妥的 Trie 字典树 模板题。

令 exist_{i,j} 表示 最后一个字母编号为 i 的单词在第 j 篇文章中是否出现

代码演示

#include <iostream>
#include <cstring>
using namespace std;
int n, m;
int trie[500010][26];   // 字典树 
int tot = 0;   // 最大节点编号
bool exist[500010][1010];
char str[1010];
int mapping(char a) {   // 映射字符 
	return a - 'a';
}
void insert(int line, char *str) {   // 将 str 单词加入字典树 
	int u = 0;   // 根节点 
	int len = strlen(str);   // 字符串长度 
	for (int i = 0; i < len; i++) {
		int a = mapping(str[i]);   // 映射字符 
		if (trie[u][a] == 0)   // 没有这个节点 
			trie[u][a] = ++tot;   // 记录该节点 
		u = trie[u][a];   // 跳至该节点 
	} 
	exist[u][line] = 1;   // 标记该单词在第 line 行出现过
}
void find(char *str) {
	int u = 0;   // 根节点 
	int len = strlen(str);   // 字符串长度 
	for (int i = 0; i < len; i++) {
		int a = mapping(str[i]);   // 映射字符
		if (trie[u][a] == 0) {   // 没有这个节点 
			cout << endl;
			return;
		}
		u = trie[u][a];
	} 
	for (int i = 1; i <= n; i++)
		if (exist[u][i]) cout << i << " ";
	cout << endl;
}
int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		int l;
		cin >> l;
		while (l--) {
			cin >> str;
			insert(i, str);
		}
	} 
	cin >> m;
	while (m--) {
		cin >> str;
		find(str);
	}
	return 0;
}

提交记录

90 pts

思路

把 exist 数组开到 50010\times 110,用 1 个点的 WA 换 10 个点的 MLE,聪明如我 QaQ!

代码演示

提交记录

100 pts

思路

绞(kan)尽(le)脑(ti)汁(jie),才发现要用到 bitset

把 exist 数组的类型改为 bitset,节省 32 倍空间,轻松 AC。

代码演示

#include <iostream>
#include <cstring>
#include <bitset>   // 别忘了导入头文件
using namespace std;
#define MAXN 1010
int n, m;
int trie[500010][26];   // 字典树 
int tot = 0;   // 最大节点编号
bitset <1010> exist[500010];
char str[1010];
int mapping(char a) {   // 映射字符 
	return a - 'a';
}
void insert(int line, char *str) {   // 将 str 加入字典树 
	int u = 0;   // 根节点 
	int len = strlen(str);   // 字符串长度 
	for (int i = 0; i < len; i++) {
		int a = mapping(str[i]);   // 映射字符 
		if (trie[u][a] == 0)   // 没有这个节点 
			trie[u][a] = ++tot;   // 记录该节点 
		u = trie[u][a];   // 跳至该节点 
	} 
	exist[u][line] = 1;   // 标记
}
void find(char *str) {
	int u = 0;   // 根节点 
	int len = strlen(str);   // 字符串长度 
	for (int i = 0; i < len; i++) {
		int a = mapping(str[i]);   // 映射字符
		if (trie[u][a] == 0) {   // 没有这个节点 
			cout << endl;
			return;
		}
		u = trie[u][a];
	} 
	for (int i = 1; i <= n; i++)
		if (exist[u][i]) cout << i << " ";
	cout << endl;
}
int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		int l;
		cin >> l;
		while (l--) {
			cin >> str;
			insert(i, str);
		}
	} 
	cin >> m;
	while (m--) {
		cin >> str;
		find(str);
	}
	return 0;
}

提交记录

个人 OI 水平有限,请见谅。 

--The End--

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值