1,字典树的概念图
由一个节点,通过数组形式(字母)走向下一个节点。
k=ch[p][q]:节点p通过路径q走到节点k
a,创建字典树
//创建字典树
int ch[100][27];//100是当前节点代表数,27是小写字母的映射;
char s[100];
int n;
int idx=0;//创建节点用;
int q = 0;//创建新节点q
int cnt[100];//计数
for (int i = 1; i <= n; i++) {
scanf("%c", &s[i]);//输入字母
int p = s[i] - 'a';//将26个字母映射成0-25
if (!ch[q][p]) ch[q][p]=++idx; //判断是否指向下一节点,如果没指向,创建新节点;
q= ch[q][p]; //指向下一节点
}
cnt[p]++;//记录插入次数
如上图,要创造从节点1通过a走向节点2的方法:
首先q=1;//节点1
idx=1;
输入s[i]='a'; 则p=0;
ch[1][0]是等于0的,执行ch[1][0]=+idx(2);
然后让p也变成2;
i走完后那个节点计数加1;
cnt表示那个节点被插入过多少次,例如插入了cat,car,ca就被插入2次
b,搜索字典树
代码类似
int ch[100][27];//100是当前节点代表数,27是小写字母的映射;
char s[100];
int n;
int q = 0;//从节点0走起
for (int i = 1; i <= n; i++) {
scanf("%c", &s[i]);//输入字母
int p = s[i] - 'a';//将26个字母映射成0-25
if (!ch[q][p])return; //判断是否指向下一节点
q= ch[q][p]; //指向下一节点
}
return cnt[p];//返回插入次数
c,练习
1,洛谷 p8306【模板】字典树 - 洛谷
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef unsigned long long ull;
const int maxn = 3000006;
const int k = 100006;
int t, n, q=0;
char s[maxn];
int p = 0;
//分别将符号映射
int start(char x) {
if (x >= 'a' && x <= 'z')x -= 'a';
else if (x >= 'A' && x <= 'Z')x -= 'A'-26;
else x -= '0' - 52;
return x;
}
//插入字符串
int cnt[maxn];//注意不要开小了
int idx = 0;
int ch[maxn][100];
void insert(char s[]) {
q = 0;
int len = strlen(s);
for (int i = 0; i < len; i++) {
p=start(s[i]);
if (!ch[q][p])ch[q][p] = ++idx;//ch[q][p]一开始都是0,从0一次建立节点
q = ch[q][p];
cnt[q]++;
}
}
//搜索字符串
int find(char s[]) {
p = 0, q = 0;
int len = strlen(s);
for (int i = 0; i < len; i++) {
p = start(s[i]);
if (!ch[q][p])return 0;
q = ch[q][p];
}
return cnt[q];
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
for (int i = 0; i <= idx; i++) {
for (int j = 0; j <= 100; j++) {
ch[i][j] = 0;
}
}
for (int i = 0; i <= idx; i++)cnt[i] = 0;
idx = 0;
int h;
scanf("%d%d", &n, &h);
for (int i = 1; i <= n; i++) {
scanf("%s", s);
insert(s);
}
for (int i = 1; i <= h; i++) {
scanf("%s", s);
printf("%d\n", find(s));
}
}
return 0;
}
2,洛谷 p2580 于是他错误的点名开始了 - 洛谷
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef unsigned long long ull;
int n, m;
const int maxn = 1e4 + 10;
const int k = 1e5 + 10;
char s[55];
ull idx=0;
int p, q;
//映射字符
int getnum(char x) {
x = x - 'a';
return x;
}
//插入字符串
int ch[k*50][100];//记得开大些,节点数最大为50*k
int cnt[k*50];
void insert(char s[]) {
int p = 0,q=0;
int len = strlen(s);
for (int i = 0; i < len; i++) {
q=getnum(s[i]);
if (!ch[p][q])ch[p][q] = ++idx;
p = ch[p][q];
}
cnt[p] = 1;
}
//查找字符串
int find(char s[]) {
p = 0;
int len = strlen(s);
for (int i = 0; i < len; i++) {
q = getnum(s[i]);
if (!ch[p][q])return 0;
p = ch[p][q];
}
if(cnt[p]==2)
return 2;
else if(cnt[p]==1) {
return 1;
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%s", s);
insert(s);
}
scanf("%d", &m);
for (int i = 1; i <= m; i++) {
scanf("%s", s);
if (!find(s))printf("WRONG\n");
else if (find(s)==1) {
cnt[p]=2;
printf("OK\n");
}
else {
printf("REPEAT\n");
}
}
return 0;
}
这道题和上道题基本类似;
不同的是不用再记录插入次数,只需要把插入过做一个标记1就行。
在访问时把访问过的数更新成2,就完成此题;
(题解中用map做法更为简单)