Trie树(字典树)

含义:

Trie树一般指字典树,又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。

假设我们有五个字符串,code、cook、five、file、fat。现在需要在里面多次查找某个字符串是否存在。如果每次查找是拿查找的字符串和这5个字符串一次进行匹配,那么效率就会比较低。
如果将这五个字符串组织成字典树,就是下面这个图,查找起来会更快。
Trie树(字典树)_第1张图片
性质:

  • 根节点不包含字符,除根节点外每一个节点都只包含一个字符
  • 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串
  • 每个节点的所有子节点包含的字符都不相同

基本操作:

查找、插入和删除,当然删除操作比较少见。

Trie树构造
Trie树(字典树)_第2张图片
Trie树的插入操作
Trie树(字典树)_第3张图片

Trie树的插入操作:是将单词的每个字母逐一插进Trie树中。插入前先看该字母对应的结点是否已经存在,若存在共享结点,则不用创建对应的结点,若不存在,就需要创建一个新结点。最后,我们在最后一个字母结点进行标记(即图中的黄色结点代表标记)。(比如插入cook后,要对k所在的结点进行标记,代表c->o->o->k这条路径上的所有结点可以组成一个单词cook)

Trie树的查询操作:比如我们查找code,可以将要查找的单词code分割成单个的字母c、o、d、e,然后从Trie树的根结点开始匹配,直到每个结点都存在(需在一条路径上)且最后一个结点被标记,才算查找成功。如图所示,绿色的路径就是在Trie树中匹配的路径。
Trie树(字典树)_第4张图片


835. Trie字符串统计
题目描述
维护一个字符串集合,支持两种操作:

“I x”向集合中插入一个字符串x;
“Q x”询问一个字符串在集合中出现了多少次。
共有N个操作,输入的字符串总长度不超过 10^5,字符串仅包含小写英文字母。

输入格式
第一行包含整数N,表示操作数。

接下来N行,每行包含一个操作指令,指令为”I x”或”Q x”中的一种。

输出格式
对于每个询问指令”Q x”,都要输出一个整数作为结果,表示x在集合中出现的次数。

每个结果占一行。

数据范围
1≤N≤2∗10^4

输入样例:
5
I abc
Q abc
Q ab
I ab
Q ab

输出样例:
1
0
1

#include
using namespace std;

const int N=1e5+10;
int son[N][26],cnt[N],idx;
char str[N];
//son[N][26]:存的值是儿子结点对应的idx
//son[N][26]:一维下标是父节点的idx,二维下标是这个父节点的直接子节点的str[i]-'a'的值

//cnt[N]:以'abc'为例, 最后一个字符'c'对应的idx作为cnt的下标。 
//cnt[N]:存的值是该以该idx结尾的字符串的个数,即有几个'abc'字符串 

//idx代表当前字符的编号,根结点为0 

void insert(char str[]) //插入字符串 
{
	int p=0;
	for(int i=0;str[i];i++) //对这个单词的每个字母依次进行插入 
	{
		int u=str[i]-'a';  //将a~z转换成0~25 
		if(!son[p][u]) son[p][u]=++idx; //如果这个字母没有就添加一个,给它一个编号 
		p=son[p][u];       //继续检索 
	}
	cnt[p]++; //单词'abc'插入之后,将最后一个字符c的cnt++,代表以c结尾的单词多了一个 
}

int query(char str[]) //查询字符串出现的次数 
{
	int p=0;
	for(int i=0;str[i];i++)
	{
		int u=str[i]-'a'; //将a~z转换成0~25
		if(!son[p][u]) return 0; //有某个结点不存在直接结束 
		p=son[p][u];  //这个结点存在就继续往下走 
	}
	return cnt[p];  
}

int main()
{
	int n;
	cin>>n;
	while(n--)
	{
		char op;
		cin>>op>>str;
		if(op=='I') insert(str);
		else cout<<query(str)<<endl;
	}
	return 0;
}

举个例子:对①~⑤个字符串依次进行插入,那么每个字符对应的idx就为图中绿色的数字。
这题建议自己画一个字典树,然后按照你画的字典树把代码顺一遍,这样会好理解些…我是这么看的
Trie树(字典树)_第5张图片


AcWing 143. 最大异或对
Trie树(字典树)_第6张图片

#include
using namespace std;
const int N = 31*1e5 + 10;
int a[N], son[N][2], idx;

void insert(int x)
{
    int p = 0;
    for(int i=30; i>=0; i--)  //先插入最高位
    {
        int u = x>>i & 1;
        if(!son[p][u]) son[p][u] = ++idx;
        p = son[p][u];
    }
}

int query(int x) 
{
    int p = 0, res = 0;
    for(int i=30; i>=0; i--)
    {
        int u = x>>i & 1;
        if(son[p][!u])
        {
            res = res*2 + !u;
            p = son[p][!u];
        }
        else
        {
            res = res*2 + u;
            p = son[p][u];
        }
    }
    return res;
}

int main()
{
    int n;
    cin >> n;
    
    int res = 0;
    for(int i=0; i<n; i++) //对于每一个a[i]先插入,再查询 a[0~i-1] 的数
    {
        cin >> a[i];
        insert(a[i]); 
        int t = query(a[i]); // 查询 a[0~i-1] 的数
        res = max(res,t ^ a[i]);
    }
    cout << res;
}

借鉴: 可以看看下面这篇,写的很详细。
看动画轻松理解「Trie树」

你可能感兴趣的:(#,算法:数据结构)