Trie——解决字符串搜索、异或最值问题

Trie——解决字符串搜索、异或最值问题

  • 在说到Trie以前,咱们设想以下问题:

给咱们1e5个由小写字母构成的不重复的字符串,每一个字符串长度不超过6,以后是1e5次查询操做,每次给咱们一个字符串,要求咱们判断这个字符串是否出现过,若是是则求出它是多少个其余的字符串的前缀,并在以后的操做中无视这个字符串(删除)。ios

  • 查询是否出现这个能够用set或者hash,可是前缀,,其实也有办法,可是这里要介绍的方法是使用一种易于理解的数据结构——Trie

创建Trie

  • 字典树Trie的结构比较天然,如对于字符串集合{"abca", "ab", "bcd", "abcde", "bcde", "bcdf"},能够创建一棵这样的Trie:

可知:数据结构

- 每一条边表明一个字符
- 节点不为0表明从根节点到此为一个完整的字符串

实现的方法也比较简单,创建一棵单向树,每一个节点都有spa

  • 26个子节点(全部小写字母);code

  • 一个isstr,bool值,表明这里是否为一个字符串的结束blog

  • 一个vis,int值,表明到这里已经有多少个字符串遍历过(是多少字符串的前缀)排序

一开始树为空,咱们每获得一个字符串,就从它的第一个字符开始,从根节点遍历,没有对应的节点就建立,同时把所通过的节点的vis值加一,到最后字符串终止时,在终止的节点处置isstr为true。字符串

更多操做

1.查询

从树的形状就能够看出,这是一棵专门查询字符串存在与否的数据结构(同时也付出了巨大的空间代价)。查询操做很简单,从根节点开始,按照要查询的字符串的每一位来遍历,若是遇到空节点或者终止时的节点的isstr为false,则字符串不存在,不然存在。get

2.查询某个串是多少个字符串的前缀

这个就是读取要查询的字符串的终止节点的vis值便可string

3.删除某个字符串

首先查询成功以后,咱们从底部开始回溯删除这个串的信息,将终止节点的isstr置为false,同时将路过的vis值减一,若是vis值减为0则将将此节点在其父节点中删除便可。hash

01Trie解决xor最值问题

原题连接

题意


给咱们一个序列A和序列B,要求咱们找到B序列的一种排列,使得

\[C_i \quad xor \quad B_i == A_i \]

中的序列C字典序最小,并输出序列C,长度<=3e5,每一个数小于2^30

思路

  • 首先按照运算规则C xor B == A意味着A xor B == C 也就是说 寻找一种B的排列,使得A逐个与B异或的结果的字典序最小

  • 3e5基本不可能在其余操做中间搞什么排序了,应该从异或运算的结果出发,咱们追求结果的字典序最小,也就是说,对于每一个Ai,咱们都要找到一个Bj,使得其异或结果最小。这个若是直接找的话,是比较难的。可是若是使用Trie的话,能够直接求得每个Ai的最小结果,方法以下:

    • 对B序列创建一棵只包含01字符的Trie(也就是二叉树),咱们规定A与B的每个数都是30位二进制数,左边补零,从左边的第一位开始建树。

    • B序列的01Trie建好以后,对A序列从1到n每一个数都按位在Trie中遍历,一开始先设ans为0,以后优先走与当前本身的二进制位相同的边,若是没有则让ans或上这一位的1。(确保ans尽量地小)

    • 以后输出这个ans,并将走过的路线所表明的Bi删除掉就能够

  • 能够看出时间复杂度为

    \[O(30*n) \]

  • 对空间复杂度来讲,不要使用满二叉树的存储方式,使用动态开点的方式,空间复杂度不超过

    \[O(30*n) \]

01Trie代码以下:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

int n;
int aa[10000005][2] = {{0}}, vv[10000005][2] = {{0}}, co = 1;
int A[300005] = {0};

void add(int x)
{
	int o = 1;
	for (int i = 29; i >= 0; --i)
	{
		int bitt = (x >> i) & 1;
		if (!aa[o][bitt])
		{
			aa[o][bitt] = ++co;
		}
		++vv[o][bitt];
		o = aa[o][bitt];
	}
}

int trie(int x)
{
	int o = 1, ans = 0;
	for (int i = 29; i >= 0; --i)
	{
		int bitt = (x >> i) & 1;
		if (vv[o][bitt])
		{
			--vv[o][bitt];
			o = aa[o][bitt];
			
		}
		else
		{
			--vv[o][bitt ^ 1];
			o = aa[o][bitt ^ 1];
			ans |= (1 << i);
		}
	}
	return ans;
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i)
	{
		scanf("%d", &A[i]);
	}
	for (int i = 1; i <= n; ++i)
	{
		int xx;
		scanf("%d", &xx);
		add(xx);
	}
	for (int i = 1; i < n; ++i)
	{
		printf("%d ", trie(A[i]));
	}
	printf("%d", trie(A[n]));
	return 0;
}
相关文章
相关标签/搜索