电子字典(字典树)
P4407 [JSOI2009] 电子字典
题目描述
人们在英文字典中查找某个单词的时候可能不知道该单词的完整拼法,而只知道该单词的一个错误的近似拼法,这时人们可能陷入困境,为了查找一个单词而浪费大量的时间。带有模糊查询功能的电子字典能够从一定程度上解决这一问题:用户只要输入一个字符串,电子字典就返回与该单词编辑距离最小的几个单词供用户选择。
字符串
- 删除串中某个位置的字母;
- 添加一个字母到串中某个位置;
- 替换串中某一位置的一个字母为另一个字母。
JSOI 团队正在开发一款电子字典,你需要帮助团队实现一个用于模糊查询功能的计数部件:对于一个待查询字符串,如果它是单词,则返回
输入格式
第一行包含两个正整数
接下来的
再接下来
输出格式
输出应包括
-
表示 为字典中的单词; -
否则
表示与 编辑距离为 的单词的个数。
输入输出样例 #1
输入 #1
4 3
abcd
abcde
aabc
abced
abcd
abc
abcdd
输出 #1
-1
2
3
说明/提示
样例解释
abcd
在单词表中出现过;abc
与单词abcd
、aabc
的编辑距离都是 ;abcdd
与单词abcd
、abcde
、abced
的编辑距离都是 。
数据范围与约定
- 所有单词互不相同,但是查询字符串可能有重复;
- 对
的数据范围, ; - 对
的数据范围, 。
分析:
一部分单词存在字典树树中,查询一个字符串有几个单词可以通过增添,替换,删除一个字符的一个操作得到。
可以看作是对查询的字符串进行一个操作,查询字典树是否有这个单词。使用dfs进行搜索,首先需要考虑dfs的参数
- 现在操作的字符在字典树的哪个位置,因此需要变量
p
- 处理到了字符串的哪个字符,需要标明字符串位置
lenth
- 是否有已经编辑过一次字符串了
f
所以使用void dfs(int p,int lenth,int f)
操作:
- 删除,直接相当于跳过该字符:
dfs(p, lenth+1, 1)
- 替换,可以使用循环遍历26个字母,在
son[p][i]
存在的情况下搜索下一个字符dfs(son[p][i], lenth+1, 1)
- 增添,也是遍历26个字母,可以和替换一起,
dfs(son[p][i], lenth, 1)
- 此外
#include <bits/stdc++.h>
//#define int long long
using namespace std;
const int N = 2e5 + 10;
int n,m,son[N][26],cnt[N],idx,lens,vis[N],ans,flag; //vis记录单词是否已经到达过,一样的单词不能算两个,别忘了每次查询清空!
char s[22];
inline void ins(char s[]) { //插入字典树
int len = strlen(s),p = 0;
for (int i = 0;i < len;++i) {
int t = s[i] - 'a';
while (!son[p][t]) son[p][t] = ++idx;
p = son[p][t];
}
cnt[p]++; //标记单词末尾
}
void dfs(int p,int lenth,int f) {
if ( lenth == lens && cnt[p] && !f) { //对于直接得到的单词
cout << -1 << '\n';
flag = 1;
return;
}
if (lenth == lens && cnt[p] && !vis[p]) { //对于修改后得到的单词
vis[p] = 1;
ans++;
return;
}
if (!f) { //在没有改动过的前提下
if (lenth < lens) dfs(p,lenth+1,1); //删除一个字符,注意删除条件
for (int i = 0;i < 26;++i) {
if (son[p][i]) dfs(son[p][i],lenth,1); //增添操作
if (i != s[lenth]-'a' && son[p][i]) dfs(son[p][i],lenth+1,1); //替换操作,注意i对应是s[lenth] - 'a',感觉i != s[lenth] - 'a'没用,确实可以ac,但是如果替换自己会发生什么?相当于没换,浪费了修改机会,但这种情况会在其他情况出现。还是留着保险
}
}
if (lenth == lens) return; //前面循环已经有过增添了,所以不用担心在末尾增添加成为一个单词的情况
if (son[p][s[lenth] - 'a']) dfs(son[p][s[lenth] - 'a'],lenth+1,f); //正常查询的情况
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin >> n >> m;
while (n--) {
cin >> s;
ins(s);
}
while (m--) {
memset(vis,0,sizeof(vis)); //对每次查询都要重置标记数组
cin >> s;
lens = strlen(s);
ans = 0,flag = 0;
dfs(0,0,0);
if (!flag) cout << ans << '\n';
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!