一.tire树简介
ie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串,
所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。
Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
它有3个基本性质:
根节点不包含字符,除根节点外每一个节点都只包含一个字符。
从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
每个节点的所有子节点包含的字符都不相同。
二.Tire树结构示意图如下(红点标记的是一个单词的结尾),我们可以很清楚的看到,树中包含了所有的单词,
单词起点都是第一行,所以说Tire树利用的是公共前缀。
三.实现方法(其实Tire树的写法很模板化)
首先我们需要一个结构体来保存每个节点的子树(下面的写法是给一些单词,查询某个单词在不在所给的词里)
这样Tire树的基本操作就完成了,当然在应用的时候还是得有些变化
四.我们现在看几个例题
HDU1251 --- 统计难题
HDU1247 --- Hat's Words
ie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串,
所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。
Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
它有3个基本性质:
根节点不包含字符,除根节点外每一个节点都只包含一个字符。
从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
每个节点的所有子节点包含的字符都不相同。
二.Tire树结构示意图如下(红点标记的是一个单词的结尾),我们可以很清楚的看到,树中包含了所有的单词,
单词起点都是第一行,所以说Tire树利用的是公共前缀。
三.实现方法(其实Tire树的写法很模板化)
首先我们需要一个结构体来保存每个节点的子树(下面的写法是给一些单词,查询某个单词在不在所给的词里)
struct tire
{
tire *next[maxn]; //保存子树节点,maxn为多大看题意,0-9的数字10就可以了,小写字母26就可以了
bool isword; //标记单词结尾
}* root; //root是根节点
然后有一个插入的单词的函数,这个很容易
void insert(char *s)
{
tire *head = root;
for(int i = 0;s[i] != '\0';++i)
{
int id = s[i]-'a'; //减去多少得看题意
if(head->next[id] == NULL) //为NULL时分配内存
{
head->next[id] = new tire();
head->next[id]->isword = false;
}
head = head->next[id];
}
head->isword = true;
}
接下来我们需要一个查询单词的函数
bool query(char *s)
{
tire *head = root;
for(int i = 0;s[i] != '\0';++i)
{
int id = s[i]-'a';
if(head->next[id] == NULL) //为空时说明找不到了
return false;
head = head->next[id];
}
if(head->isword)
return true;
return false;
}
最后就是需要一个释放内存的函数,对于多组测试,这个是很有必要的,否则很可能爆内存
<span style="white-space:pre"> </span>void del(tire *head)
{
for(int i = 0;i < maxn;++i)
{
if(head->next[i] != NULL)
del(head->next[i]);
}
delete head;
}
这样Tire树的基本操作就完成了,当然在应用的时候还是得有些变化
四.我们现在看几个例题
HDU1251 --- 统计难题
http://acm.hdu.edu.cn/showproblem.php?pid=1251
只需把结构体里的bool isword;改成一个整型变量保存这个字串出现的次数就可以啦
/*
author: tpbluesky
time:
题解:
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <sstream>
#define inf 0x3f3f3f3f
#define eps 1e-8
#define sqr(x) ((x)*(x))
using namespace std;
typedef long long ll;
struct trie
{
trie *next[26];
int num;
};
trie *root;
void insert(char *s)
{
trie *head = root;
for(int i = 0;s[i] != '\0';i++)
{
int id = s[i]-'a';
if(head->next[id] == NULL)
{
head->next[id] = new trie();
head->next[id]->num = 1;
}
else
{
head->next[id]->num++;
}
head = head->next[id];
}
}
int search(char *s)
{
trie *head = root;
for(int i = 0;s[i] != '\0';i++)
{
int id = s[i]-'a';
if(head->next[id] == NULL)
return 0;
head = head->next[id];
}
return head->num;
}
int main()
{
char s[15];
root = new trie();
while(gets(s) && s[0] != '\0')
insert(s);
while(gets(s) != NULL)
cout<<search(s)<<endl;
return 0;
}
HDU1671 phone list
http://acm.hdu.edu.cn/showproblem.php?pid=1671
注意要用del函数就可以了
/*
author: tpbluesky
time:
题解:
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <sstream>
#define inf 0x3f3f3f3f
#define eps 1e-8
#define sqr(x) ((x)*(x))
using namespace std;
typedef long long ll;
const int maxn = 10;
struct tire
{
tire *next[10];
bool isword;
};
tire *root;
int insert(char *s)
{
tire *head = root;
for(int i = 0;s[i] != '\0';++i)
{
int id = s[i]-'0';
if(head->next[id] == NULL)
{
head->next[id] = new tire();
head->next[id]->isword = false;
}
else
{
if(head->next[id]->isword)
return 1;
}
head = head->next[id];
}
for(int i= 0;i < 10;++i)
if(head->next[i] != NULL)
return 1;
head->isword = true;
return 0;
}
void del(tire *head)
{
for(int i = 0;i < 10;++i)
{
if(head->next[i] != NULL)
del(head->next[i]);
}
delete head;
}
int main()
{
int T, n;
char s[15];
cin>>T;
while(T--)
{
root = new tire();
scanf("%d",&n);
int flag = 1;
for(int i = 0;i < n ;++i)
{
scanf("%s",s);
if(flag && insert(s))
{
flag = 0;
}
}
printf(flag?"YES\n":"NO\n");
del(root);
}
return 0;
}
HDU1247 --- Hat's Words
http://acm.hdu.edu.cn/showproblem.php?pid=1247
查找时把单词拆开成两个单词,分别找就可以了,然后就是按字典序输出
/*
author: tpbluesky
time:
题解:
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <sstream>
#define inf 0x3f3f3f3f
#define eps 1e-8
#define sqr(x) ((x)*(x))
using namespace std;
typedef long long ll;
const int maxn = 50005;
char s[maxn][20];
struct tire
{
tire *next[26];
bool isword;
};
tire *root;
void insert(char *s)
{
tire *head = root;
for(int i = 0;s[i] != '\0';++i)
{
int id = s[i]-'a';
if(head->next[id] == NULL)
{
head->next[id] = new tire();
head->next[id]->isword = false;
}
head = head->next[id];
}
head->isword = true;
}
bool search(char *s)
{
tire *head = root;
int len = strlen(s);
for(int i = 0;i < len;++i)
{
int id = s[i]-'a';
if(head->next[id] == NULL)
return false;
head = head->next[id];
}
if(head->isword)
return true;
return false;
}
set<string> se;
int main()
{
char str[100];
int cnt = 0;
root = new tire();
while(scanf("%s",str) == 1)
{
strcpy(s[cnt++],str);
insert(str);
}
for(int i = 0;i < cnt;++i)
{
int len = strlen(s[i]);
for(int j = 1;j < len;++j)
{
strcpy(str,s[i]+j);
char s1[15];
strcpy(s1,s[i]);
s1[j] = '\0';
if(search(s1) && search(str))
se.insert(string(s[i]));
}
}
for(set<string>::iterator it = se.begin();it != se.end();++it)
cout<<*it<<endl;
return 0;
}
http://acm.hdu.edu.cn/showproblem.php?pid=4287
查找的方法注意一下就可以啦,有的是一对三位,有的是一对四位,用递归比较好些
/*
author: tpbluesky
time:
题解:
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <sstream>
#define inf 0x3f3f3f3f
#define eps 1e-8
#define sqr(x) ((x)*(x))
using namespace std;
typedef long long ll;
const int maxn = 5005;
char s[maxn][10];
int n, m, cnt;
char stemp[10][10]={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
struct tire
{
tire *next[26];
int isword;
};
tire *root;
void insert(char *s)
{
tire *head = root;
for(int i = 0;s[i] != '\0';++i)
{
int id = s[i]-'a';
if(head->next[id] == NULL)
{
head->next[id] = new tire();
head->next[id]->isword = 0;
}
head = head->next[id];
}
head->isword++;
}
void search(tire *head,char *s,int cur)
{
if(cur >= strlen(s))
{
cnt += head->isword;
return;
}
int id = s[cur]-'0';
for(int i = 0;i < strlen(stemp[id]);++i)
{
int t = stemp[id][i]-'a';
if(head->next[t] != NULL)
search(head->next[t],s,cur+1);
}
}
void del(tire *head)
{
for(int i = 0;i < 26;++i)
{
if(head->next[i] != NULL)
del(head->next[i]);
}
delete head;
}
int main()
{
char s1[10];
int T;
cin>>T;
while(T--)
{
scanf("%d%d",&n,&m);
root = new tire();
for(int i = 0;i < n;++i)
scanf("%s",s[i]);
for(int i = 0;i < m;++i)
{
scanf("%s",s1);
insert(s1);
}
for(int i = 0;i < n;++i)
{
cnt = 0;
search(root,s[i],0);
printf("%d\n",cnt);
}
del(root);
}
return 0;
}
考虑到查询的串是子串,而不是前缀字串,因此把所有字串也加到Tire树里面,但是统计又出现问题了,可能会重复统计
可以在结构体里加一个变量,记录当前是操作哪个串,如果操作的同一串,就不用统计,不是同一串就统计,并且更新记录到当前串
/*
author: tpbluesky
time:
题解:
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <sstream>
#define inf 0x3f3f3f3f
#define eps 1e-8
#define sqr(x) ((x)*(x))
using namespace std;
typedef long long ll;
struct tire
{
tire *next[26];
int v, num;
} *root;
void insert(char *s,int k)
{
tire *head = root;
for(int i = 0;s[i] != '\0';++i)
{
int id = s[i]-'a';
if(head->next[id] == NULL)
{
head->next[id] = new tire();
head->next[id]->v = 1;
head->num = k;
}
else
{
if(k != head->next[id]->num)
{
head->next[id]->v++;
head->next[id]->num = k;
}
}
head = head->next[id];
}
}
int query(char *s)
{
tire *head = root;
for(int i = 0;s[i] != '\0';++i)
{
int id = s[i]-'a';
if(head->next[id] == NULL)
return 0;
head = head->next[id];
}
return head->v;
}
int main()
{
int n, m;
char s[25], ss[25];
scanf("%d",&n);
root = new tire();
for(int i = 0;i < n;++i)
{
scanf("%s",s);
for(int j = 0;j < strlen(s);++j)
{
strcpy(ss,s+j);
insert(ss,i);
}
}
scanf("%d",&m);
for(int i = 0;i < m;++i)
{
scanf("%s",s);
printf("%d\n",query(s));
}
return 0;
}