tire tree是一种存储字符串的数据结构,我们可以通过它来进行插入,查询,删除等操作
首先其核心思想就是把每一个字符串中的每一个字符抽象成一棵树中的一条边,以及这条边所连接的子节点,插入时只需沿着相应前缀的字母向下走,一旦无法再继续走,也就是说当前树中无这个字符串,那么我们可以新建一个节点和一条边,表示没有的字母,来得到插入的字符串。查询时同样是沿着字母走,一旦没有,则返回一个值,有的话返回另一个值即可。
下面是各种操作的代码:
//temp表示当前所处节点,sum表示相应节点所对应的字符串前缀的个数,p是把字符转换成数字,tire[i][j]表示当前位于i节点,沿着字母j这条边能走到的点的编号
//若为0,则说明不存在,index表示动态建立节点的编号,v表示相应的字符串被插入的次数
void insert() {
cin >> s;
int i, l = s.size(), temp = 1;//开始位于根节点
sum[1]++;
for (i = 0; i < l; i++) {
int p = s[i]-'a'+1;
if (!tire[temp][p]) tire[temp][p]=++index;//f[index] = temp;这个f可以确定父节点,注意index要从根节点开始加
temp = tire[temp][p], sum[temp]++;
}
v[temp]++;
return ;
}
int find() {
cin >> s;
int i, l = s.size(), temp = 1;
for (i = 0; i < l; i++) {
int p = s[i]-'a'+1;
if (!tire[temp][p]) return 0;
temp = tire[temp][p];
}
return sum[temp];//v[temp] 可以查找前缀出现的次数和字符串出现的次数
}
//删除操作与插入类似,把沿路加给为沿路减即可,注意删除后并不需要将已经开出来的点再删掉
void deletes() {
cin >> s;
int i, l = s.size(), temp = 1;
sum[1]--;
for (i = 0; i < l; i++) {
int p = s[i]-'a'+1;
if (!tire[temp][p]) return;
temp = tire[temp][p], sum[temp]--;
}
v[temp]--;
return ;
}
在tire tree中一个字符串对应于树的一条路。
练习题:https://www.luogu.org/problemnew/show/P2580
code:
#include<iostream>
#include<cstdio>
using namespace std;
const int N = 1e4+10, M = 1e5+10, MAX = 1e6;
int n, m;
int tire[MAX][30], sum[MAX], v[MAX], index = 1;
string s1[N], s2[M];
void insert(int i) {
int j, l = s1[i].size(), temp = 1;
sum[1]++;
for (j = 0; j < l; j++) {
int p = s1[i][j]-'a'+1;
if (!tire[temp][p]) tire[temp][p]=++index;//f[index] = temp;
temp = tire[temp][p], sum[temp]++;
}
v[temp]++;
return ;
}
int find(int i) {
int j, l = s2[i].size(), temp = 1;
for (j = 0; j < l; j++) {
int p = s2[i][j]-'a'+1;
if (!tire[temp][p]) return 0;
temp = tire[temp][p];
}
return temp;
}
int main() {
cin >> n;
int i;
for (i = 1; i <= n; i++) {
cin >> s1[i];
insert(i);
}
cin >> m;
for (i = 1; i <= m; i++) {
cin >> s2[i];
int temp = find(i);
if (v[temp] == 0) printf("WRONG\n");
if (v[temp] == 1) {
printf("OK\n");
v[temp]++;
continue;
}
if (v[temp] > 1) printf("REPEAT\n");
}
return 0;
}