《算法竞赛进阶指南》0x16Trie


Trie(字典树)是一种用于实现字符串快速检索的多叉树结构。Trie的每个节点都拥有若干个字符指针,若在插入或检索字符串时扫描到了一个字符c,就沿着当前节点的c字符指针,走到该指针指向的节点。

初始化
一颗空Trie仅包含一个根节点,该点的字符指针均指向空。
插入
当需要插入一个字符串S时,我们令一个指针P起初指向根节点。然后,依次扫描S中的每个字符c。
1.若P的c字符指针指向一个已经存在的节点Q,则令P=Q。
2.若P的c字符指针指向空,则新建一个节点Q,令P的c指针指向Q,然后令P=Q。
当S的字符扫描完毕之后,在当前节点P上标记他是一个字符串的末尾。
检索
当需要检索一个字符串S在Trie是否存在时,我们令一个指针p起初指向根节点,然后依次扫描S中的每个字符c:
1.若p的c字符指向空,则说明S没有被插入过Trie,结束检索。
2.若p的c字符指向一个已经存在的节点Q,则令P=Q。

当S中的字符扫描完毕时,若当前节点P被标记为一个字符串的末尾,则说明S在Trie中存在,否则说明S没有查如果Trie。

int trie[SIZE][26],tot=1;//初始化,假设字符串由小写字母构成
void insert(char*str)//插入一个字符串 
{
	int len=strlen(str),p=1;
	for(int ik=0;k<len;k++)
	{
		int ch=str[k]-'a';
		if(trie[p][ch]==0)trie[p][ch]=++tot;
		p=trie[p][ch];
	}
	ed[p]=true;
}

bool search(char*str)//检索字符串是否存在 
{
	int len=strlen(str);
	for(int k=0;k<len;k++)
	{
		int ch=str[k]-'a';
		p=trie[p][ch]; 
		if(p==0)return false;
	}
	return ed[p];
}

例题

acwing142.前缀统计

模板题,只需要增加一个cnt数组。

#include<iostream>
#include<cstring>
using namespace std;
int n,m;
char s[1000005];
int trie[1000005][26],tot=1;
int cnt[1000005];
void insert(char*s)
{
    int len=strlen(s),p=1;
    for(int i=0;i<len;i++)
    {
        int ch=s[i]-'a';
        if(trie[p][ch]==0)trie[p][ch]=++tot;
        p=trie[p][ch];
    }
    cnt[p]+=1;
    return ;
}
int search(char*s)
{
    int ans=0;
    int len=strlen(s),p=1;
    for(int i=0;i<len;i++)
    {
        p=trie[p][s[i]-'a'];
        if(p==0)return 0;
        ans+cnt[p];
    }
    return ans;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s);
        insert(s);
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%s",s);
        cout<<search(s)<<endl;
    }
    return 0;
}

acwing143.最大异或对

把每一个以二进制数的形式插入Trie,在search的过程中,尽可能走和当前位相反的那一个分支,如果走不通再走和当前位相同的分支,最终可以得到最大异或值。

#include<iostream>
using namespace std;
#define MAX_N 100000
int n;
int trie[MAX_N*31][2];
int tot=0;
int num[MAX_N+5];
void insert(int num)
{
    int p=0;
    for(int i=30;i>=0;i--)
    {
        int now=num>>i&1;
        if(trie[p][now]==0)trie[p][now]=++tot;
        p=trie[p][now];
    }
    return ;
}
int search(int num)
{
    int p=0,ans=0;
    for(int i=30;i>=0;i--)
    {
        int now=num>>i&1;
        if(trie[p][1^now])
        {
            p=trie[p][1^now];
            ans+=1<<i;
        }
        else p=trie[p][now];
    }
    return ans;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>num[i];
        insert(num[i]);
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    ans=max(ans,search(num[i]));
    cout<<ans<<endl;
    return 0;
}

acwing144.最长异或值路径

两个点之间的异或和等价于从根节点到点a的异或和与从根节点到点b的异或和进行一次异或。
进行一遍深搜,找到从根节点到达所有节点的路径上的异或和。
之后可以将本题完全转化为上一题。

#include<iostream>
#include<vector>
using namespace std;
typedef pair<int,int> PII;
#define MAX_N 100000
vector<PII>ed[MAX_N+5];
int val[MAX_N+5];
int trie[MAX_N*31][2];
int tot=0;
int n;
void dfs(int now,int fa,int sum)
{
	val[now]=sum;
	for(auto k:ed[now])
	if(k.first!=fa)
	dfs(k.first,now,sum^k.second);
	return ;
}
void insert(int num)
{
	int p=0;
	for(int i=30;i>=0;i--)
	{
		int now=num>>i&1;
		if(trie[p][now]==0)trie[p][now]=++tot;
		p=trie[p][now]; 
	}
	return ;
}
int search(int num)
{
	int p=0,ans=0;
	for(int i=30;i>=0;i--)
	{
		int now=num>>i&1;
		if(trie[p][1^now])
		{
			p=trie[p][1^now];
			ans+=1<<i;
		}
		else p=trie[p][now];
	}
	return ans;
}
int main()
{
	cin>>n;
	for(int i=1,u,v,w;i<=n-1;i++)
	{
		cin>>u>>v>>w;
		ed[u].push_back({v,w});
		ed[v].push_back({u,w});
	}
	dfs(0,-1,0);
	for(int i=0;i<n;i++)
	insert(val[i]);
	int ans=0;
	for(int i=0;i<n;i++)
	ans=max(ans,search(val[i]));
	cout<<ans;
	return 0;
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
算法竞赛进阶指南》是一本进阶级别的书籍,不适合初学者阅读。根据引用中的描述,每一章都会总结书中的难点知识,并附上例题和习题。从引用的目录可以看出,《算法竞赛进阶指南》包含了基本算法、位运算、递推与递归、前缀和与差分、二分、排序、倍增、贪心等内容,还包括了基本数据结构如栈、队列、链表、Hash、字符串、Trie、二叉堆等。此外,书中还讲解了动态规划的各种子领域,如线性dp、背包、区间dp、树形dp、状态压缩dp等。对于想要深入学习算法竞赛的读者来说,《算法竞赛进阶指南》是一本很好的参考书籍。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [【算法竞赛进阶指南】学习笔记](https://blog.csdn.net/cpp_juruo/article/details/122520206)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *3* [算法竞赛进阶指南总结(一)](https://blog.csdn.net/weixin_64393298/article/details/124234703)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值