Trie树的作用是存储多个字符串, 并按照字典序排好。算法时间复杂度为O(n), 空间复杂度为O(nk), n为所有字符串的长度和, k为可能出现的字符个数。 Trie树的实现就是按照单词的字母顺序从上到下依次连接(注意根节点为空)
字典树最常见的操作是插入和查询,
其中插入是对字典树的构建,基本思路是从头遍历字符串,若发现在Trie树中已经有了相应结点,则向下走,否则新建结点
查询一般来说是求前缀多少的,基本思路也是从头遍历字符串,若能在Trie树中找到相应结点就继续往下走,找不到就return 0
下面代码是Trie树的数组方式实现:
const int alpha = 26;
const int maxn = 5e5;
struct Trie
{
int ch[maxn][alpha];
int val[maxn];
int sz;
Trie(){
sz = 1;
memset(ch[0], 0 , sizeof(ch[0]));
}
int idx(char c) {return c - 'a';}
void insert(char *s)
{
int u = 0, n = strlen(s);
for(int i = 0; i < n; i++){
int c = idx(s[i]);
if(!ch[u][c]){
memset(ch[sz], 0, sizeof(ch[sz]));
val[sz] = 0;
ch[u][c] = sz++;
}
u = ch[u][c];
val[u]++;
}
}
int findout(char *s)
{
int u = 0, cnt = 0, n = strlen(s);
for(int i = 0; i < n; i++){
int c = idx(s[i]);
if(!ch[u][c]) return 0;
u = ch[u][c];
}
return val[u];
}
}a;
其中alpha代表小写的26个英文字母, maxn为字符串总长度, sz为结点总数, ch[i][j]表示结点i的编号为j的子节点,如ch[i][0]表示结点i的子节点a,val[i]表示结点i的附加信息
为了表明模板的正确性,用HDU 1251来测试一下:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int alpha = 26;
const int maxn = 5e5;
struct Trie
{
int ch[maxn][alpha];
int val[maxn];
int sz;
Trie(){
sz = 1;
memset(ch[0], 0 , sizeof(ch[0]));
}
int idx(char c) {return c - 'a';}
void insert(char *s)
{
int u = 0, n = strlen(s);
for(int i = 0; i < n; i++){
int c = idx(s[i]);
if(!ch[u][c]){
memset(ch[sz], 0, sizeof(ch[sz]));
val[sz] = 0;
ch[u][c] = sz++;
}
u = ch[u][c];
val[u]++;
}
//val[u]++;
}
int findout(char *s)
{
int u = 0, cnt = 0, n = strlen(s);
for(int i = 0; i < n; i++){
int c = idx(s[i]);
if(!ch[u][c]) return 0;
u = ch[u][c];
}
return val[u];
}
}a;
int main()
{
char s[11];
gets(s);
while(s[0] != '\0'){
a.insert(s);
gets(s);
}
while(~scanf("%s", s)){
cout << a.findout(s) << '\n';
}
return 0;
}
再来感受一下HDU--1247吧,用的另一种数组写法:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 50005;
int tree[maxn][26];
int idx(char c){return c-'a';}
int tot = 1;
bool is_word[maxn];
void Insert(char s[],int u) //插入字符串s
{
for(int i=0;s[i];++i){
int c = idx(s[i]);
if(!tree[u][c])
tree[u][c] = ++tot;
u = tree[u][c];
}
is_word[u] = true; //标记这个单词结束
}
bool Find(char s[],int u) //查询s是否是前缀
{
for(int i=0;s[i];++i){
int c = idx(s[i]);
if(!tree[u][c])
return false;
u = tree[u][c];
}
return is_word[u];
}
char s[maxn][20],ns[20];
int main()
{
int n = 0;
while(scanf("%s",s[n])!=EOF){
Insert(s[n++],1);
}
for(int i=0;i<n;++i){
for(int j=1;s[i][j];++j){
memcpy(ns,s[i],sizeof(s[i])); //将s[i]复制到ns中
ns[j] = 0;
if(Find(ns,1)&&Find(s[i]+j,1)){ //s[i]+j:从s[i]的第j个开始
//将s分成两部分进行分别查找
cout << s[i] << '\n';
break;
}
}
}
return 0;
}