Study notes: Trie tree (dictionary tree)

Trie tree is a tree structure. A typical application is to count, sort and save a large number of strings (but not limited to strings). Its advantages are: use the common prefix of strings to reduce query time and minimize unnecessary string comparisons
Source and Baidu Encyclopedia

AcWing 835. Trie string statistics

Maintaining a string collection supports two operations:
"I x" inserts a string x into the collection;
"Q x" asks how many times a string appears in the collection.
There are N operations, the total length of the input string does not exceed 105, and the string only contains lowercase English letters.
Input format The
first line contains the integer N, which represents the operand.
Next N lines, each line contains an operation instruction, the instruction is one of "I x" or "Q x".

Output format
For each query command "Q x", an integer must be output as the result, indicating the number of times x appears in the set. Each result occupies one line (1≤N≤2∗1e4)

#include<cstdio>
#include<cmath>
#include<ctime>
#include<cstring>
#include<iostream>
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<string>
#include<vector>
#define ll long long
#define ull unsigned long long
#define up_b upper_bound
#define low_b lower_bound
#define m_p make_pair
#define mem(a) memset(a,0,sizeof(a))
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define inf 0x3f3f3f3f
#define endl '\n'
#include<algorithm>
using namespace std;

inline ll read()
{
    
    
	ll x=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9')	{
    
     if(ch=='-') f=-1; ch=getchar(); }
	while('0'<=ch&&ch<='9')	x=x*10+ch-'0', ch=getchar();
	return f*x;
}

const int N = 1e5+5;

int son[N][26],idx,cnt[N]; //cnt记录出现次数
//因为只有26个小写字母,所以第二维开26大小 
void insert(string str)
{
    
    
	int p=0;//0既是根节点,也代表空节点
	for(int i=0;i<str.length();i++)
	{
    
    
		int u=str[i]-'a';
		if(!son[p][u])	son[p][u]=++idx;//如果节点p处没有u这个分支,就创建一个
		p=son[p][u];//以儿子为父亲,继续向下走
	}
	cnt[p]++;//走到结尾后记录一下在该处结尾的字符串数量
}

int query(string str)
{
    
    
	int p=0;//从根节点开始查找
	for(int i=0;i<str.length();i++)
	{
    
    
		int u=str[i]-'a';
		if(!son[p][u])	return 0;//如果查到中途发现无路可走说明没有这个字符串
		p=son[p][u];
	}
	return cnt[p];//走到结尾后返回在该处结尾的字符串数量

}

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

Example:
AcWing 143. The largest XOR pair

Select two of the given N integers a1, a2...aN to perform the xor (exclusive OR) operation. What is the maximum result?

Input format
Enter an integer N in the first line.
Enter N integers a1 to an in the second line.
(1≤N≤1e5, 0≤ai<2^31)
Output format
Output an integer to represent the answer.


Method : Because the maximum range of n is 1e5, the O(n^2) method of violent enumeration must be timed out. Trie tree optimization is used here.
Because ai<=2^31, the longest binary number of a number is 31 bits. We can store the binary of each number in a Trie tree, and we know from the property of XOR. To maximize the XOR value of two numbers, we should try to make every bit of the binary number of these two numbers different , Of course, the priority is to make the high bits different (because the exclusive OR will make the same 0 and the difference 1).
So for a number ai, we can start from the highest bit 31 and find the current bit in the Trie tree according to its binary number representation For different numbers, from the highest digit to the lowest digit, each time it goes in the opposite direction to the current digit of ai. For example, if the xth digit of ai is 0, we will go in the direction of 1, otherwise we will go in the direction of 0. , Provided that the direction exists

#include<cstdio>
#include<cmath>
#include<ctime>
#include<cstring>
#include<iostream>
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<string>
#include<vector>
#define ll long long
#define ull unsigned long long
#define up_b upper_bound
#define low_b lower_bound
#define m_p make_pair
#define mem(a) memset(a,0,sizeof(a))
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define inf 0x3f3f3f3f
#define endl '\n'
#include<algorithm>
using namespace std;

inline ll read()
{
    
    
	ll x=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9')	{
    
     if(ch=='-') f=-1; ch=getchar(); }
	while('0'<=ch&&ch<='9')	x=x*10+ch-'0', ch=getchar();
	return f*x;
}

const int N =  1e5+5;
const int M = 3e6+5;
//因为每个数最多有31位,最多有1e5个数,极端情况会有3e6个节点
int n,a[N];
int son[M][2],idx;

void insert(int x) //插入的过程也是构建Trie树的过程
{
    
    
	int p=0;//从根节点0开始
	for(int i=30;~i;i--)//从高位到低位插入
	{
    
    
		int u=x>>i&1;
		if(!son[p][u])	son[p][u]=++idx;//如果当前节点没有该分支,就在此处创建一个分支
		p=son[p][u]; //以p的儿子为父亲继续向下走
	}
}

int query(int x)
{
    
    
	int p=0,res=0;
	for(int i=30;~i;i--)
	{
    
    
		int u=x>>i&1;
		if(son[p][!u]) //如果与s相反的方向存在就向相反的方向走
		{
    
    
			res+=1<<i; //对答案的贡献
			p=son[p][!u];
		}
		else	p=son[p][u]; //不存在只能往本身的方向走
	}
	return res;
}

int main()
{
    
    
	n=read();
	for(int i=0;i<n;i++)
	{
    
    
		a[i]=read();
		insert(a[i]);//插入到Trie树中
	}
	int ans=0;
	for(int i=0;i<n;i++)	ans=max(ans,query(a[i]));
	cout<<ans<<endl;
	return 0;
}

Guess you like

Origin blog.csdn.net/m0_50815157/article/details/113547527