数据结构基础5:Trie树

Trie树

快速存储和查找字符串集合的数据结构

abcdef
abdef
aced
bcdf
bcff
    
                root
            a       	b
          b		c		  c	
        c  d	e		d	f
        d  e   1d	   1f  1f
        e 1f
       1f
//在单词结束的地方打标记

维护一个字符串集合,支持向集合中插入一个字符串以及询问一个字符串在集合中出现了多少次

关于trie树如何用数组去建树,一维是结点总数,而结点和结点之间的关系(谁是谁儿子)存在第二个维度,比如[0][1]=3, [0]表示根节点,[1]表示它有一个儿子‘b’,这个儿子的下标是3;接着如果有一个[3][2]=8 ; 说明根节点的儿子‘b’也有一个儿子‘c’,这个孙子的下标就是8;这样传递下去,就是一个字符串。随便给一个结点[x][y], 并不能看出它在第几层,只能知道,它的儿子是谁。

Trie1.PNG

Trie2.PNG

不是按层排布的,但是可以正常查询

Trie树算法模板:

int son[N][26], cnt[N], idx;
// 0号点既是根节点,又是空节点
// son[][]存储树中每个节点的子节点,每个字母最多再往外连26条边
// cnt[]存储以每个节点结尾的单词数量,每个节点用idx表示
// idx存储当前用到了哪个下标

// 插入一个字符串
void insert(string str)
{
    int p = 0;
    for (int i = 0; i < str.size(); i ++ )
    {
        int u = str[i] - 'a';
        //找到下一条边的编号,如果未被创建就创建一下
        if (!son[p][u]) son[p][u] = ++ idx;
        p = son[p][u];
    }
    //以p结尾的单词增加
    cnt[p] ++ ;
}

// 查询字符串出现的次数
int query(string str)
{
    int p = 0;
    for (int i = 0; i < str.size(); i ++ )
    {
        int u = str[i] - 'a';
        if (!son[p][u]) return 0;
        p = son[p][u];
    }
    return cnt[p];
}

最大异或对

在给定的 N N N 个整数 A 1 , A 2 … … A N A_1,A_2……A_N A1A2……AN中选出两个进行 x o r xor xor(异或)运算,得到的结果最大是多少?

异或=不进位加法

暴力做法

int res = 0;
for(int i = 0; i < n; i ++)
{
	for(int j = 0; j < i; j ++)
    {
		res = max(res, a[i] ^ a[j]);
    }
}

第二层循环是针对于 a i a_i ai,从 a 0 a_0 a0 a i − 1 a_{i-1} ai1中寻找一个数 a j a_j aj,使得 a [ i ] ⨁ a [ j ] a[i]\bigoplus a[j] a[i]a[j]最大

Tire树优化

要使得数尽可能大,就要让高位的数尽量大,所以先判断高位

对于每个数,先在Tire树中查找异或后最大的数,再插入进Trie树中

要使得异或值最大,就要每一位都尽量不一样,顺着在Trie树中找相反的位

#include <iostream>
#include <algorithm>

using namespace std;

const int N = (1e5 + 10) * 32;

int n, res, x;
int son[N][2], idx;
// N 代表一个数字的二进制串最长有多少位

void insert(int x)
{
    int p = 0;
    for(int i = 30; i >= 0; i --)
    {
        int u = x >> i & 1;		/// 取X的第i位的二进制数
        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])  	// 如果相反的位存在的话,就尽量寻找相反的位
        {
            p = son[p][!u];
            res = res * 2 + !u;
        }
        else
        {
            p = son[p][u];
            res = res * 2 + u;
        }
    }
    return res;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n;
    while(n --)
    {
        cin >> x;
        insert(x);
        int p = query(x);
        res = max(res, p ^ x);
    }
    cout << res << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

钰见梵星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值