trie树,也称字典树,大家有没有想过你们在字典里查找一个单词是怎么样的。
首先,我们会查找第一个字符。
然后在第一个字符的范围内,查找第二个字符…以此类推
我们的字典树,就是这样的。
前置知识
字符串(都会?)
算法用途
快速搜索字符串
算法复杂度
时间
插入,删除,查询长度为 的字符串都是
空间
n次插入,最大长度len,有x个不同字符:
算法实现
插入
插入字符串 。
我们从根开始,往下走,根的深度为 ,当前深度为 ,我们看看当前节点有没有子节点 ( 的第 个字符的编号),有,继续往下走,没有,那就增加一个,然后继续走。直到执行完
查询
查询字符串str。
同理,我们从根开始,往下走,根的深度为 ,当前深度为 ,我们看看当前节点有没有子节点 ( 的第 个字符的编号),有,继续往下走,没有,就是没有这个字符串了
删除
删除字符串str。
同上,只是查找到后把只属于这个字符串的东西删了就行了。
算法优化
我们可以发现这算法时间复杂度很好,但是空间太大了。
所以我们考虑用一下奇怪的优化
正常的话要储存基本的字符串我们要每个节点
但是,我们可以考虑把一个字符拆分成 个一次插入,这样一个字符串长度就为 ,然后每个节点儿子个数就为
因为一般情况下都是 比
那原来空间为 ,现在变为
减少了 倍!当然你也可以考虑拆成4份
代码
void add(string str)
{
int p = 0;
tree[p].cnt++;
for(int i = 0; i < str.size(); i++)
{
int a = str[i] >> 4;
if(tree[p].son[a])
{
p = tree[p].son[a];
}
else
{
if(sta.empty())
{
tree[p].son[a] = ++siz;
p = siz;
}
else
{
tree[p].son[a] = sta.top();
p = tree[p].son[a];
sta.pop();
}
}
tree[p].cnt++;
a = str[i] & 0x0f;
if(tree[p].son[a])
{
p = tree[p].son[a];
}
else
{
if(sta.empty())
{
tree[p].son[a] = ++siz;
p = siz;
}
else
{
tree[p].son[a] = sta.top();
p = tree[p].son[a];
sta.pop();
}
}
tree[p].cnt++;
}
}
void del(string str)
{
int p = 0;
tree[p].cnt--;
for(int i = 0; i < str.size(); i++)
{
int a = str[i] >> 4;
if(tree[p].son[a])
{
int nowp = p;
p = tree[p].son[a];
tree[p].cnt--;
if(!tree[p].cnt)
{
tree[nowp].son[a] = 0;
sta.push(p);
}
}
else
{
break;
}
a = str[i] & 0x0f;
if(tree[p].son[a])
{
int nowp = p;
p = tree[p].son[a];
tree[p].cnt--;
if(!tree[p].cnt)
{
tree[nowp].son[a] = 0;
sta.push(p);
}
}
else
{
break;
}
}
}
例题