前缀树

用途:自动补全,拼音检查,ip路由(最长前缀匹配),九宫格打字预测

还有其他的数据结构,如平衡树和哈希表,使我们能够在字符串数据集中搜索单词。为什么我们还需要 Trie 树呢?尽管哈希表可以在 O(1)O(1) 时间内寻找键值,却无法高效的完成以下操作:

  1. 找到具有同一前缀的全部键值。
  2. 按词典序枚举字符串的数据集。

  Trie 树优于哈希表的另一个理由是,随着哈希表大小增加,会出现大量的冲突,时间复杂度可能增加到 O(n)O(n),其中 nn 是插入的键的数量。与哈希表相比,Trie 树在存储多个具有相同前缀的键时可以使用较少的空间。此时 Trie 树只需要 O(m)O(m) 的时间复杂度,其中 mm 为键长。而在平衡树中查找键值需要 O(m \log n)O(mlogn) 时间复杂度。

Trie 树是一个有根的树,其结点具有以下字段:。

  1. 最多 RR 个指向子结点的链接,其中每个链接对应字母表数据集中的一个字母。
  2. 本文中假定 RR 为 26,小写拉丁字母的数量。
  3. 布尔字段,以指定节点是对应键的结尾还是只是键前缀。
const int MAXN=;//英文字符个数
class Trie
{
private:
Trie *next[MAXN];
bool isEnd=false;
public:
/** Initialize your data structure here. */
Trie()
{
isEnd=false;
memset(next,,sizeof(next));
}
/** Inserts a word into the trie. */
void insert(string word)
{
if(word.empty())
return ; Trie *cur=this;//cur初始化根节点
for(auto c:word)
{
if(cur->next[c-'a']==nullptr)//看当前结点在前缀树中是否存在
{
Trie *node=new Trie();
cur->next[c-'a']=node;
}
cur=cur->next[c-'a'];//每个结点有个next和isEnd
}
cur->isEnd=true;//当前节点已经是一个完整的字符串
return ;
}
/** Returns if the word is in the trie. */
bool search(string word)
{
if(word.empty())
return false; Trie *cur=this;
for(auto c:word)
{
if(cur)
cur=cur->next[c-'a'];//若c在Trie中不存在,则cur->next[c-'a']为nullptr
}
return cur&&cur->isEnd?true:false;//cur不为空且cur指向的结点为一个完整的字符串,则为成功找到
}
/** Returns if there is any word in the trie that starts with the given prefix. */
bool startsWith(string prefix)
{
if(prefix.empty())
return false; auto cur=this;
for(auto c:prefix)
{
if(cur)
cur=cur->next[c-'a'];
}
return cur?true:false;
}
};

  异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1),这些法则与加法是相同的,只是不带进位,所以异或常被认作不进位加法。

#include <iostream>
#include <vector>
#include <cmath>
using namespace std; //1.暴力解O(n^3)
int get_max_eor(const vector<int>& arr)
{
if(arr.empty()||arr.size()<)
return -; int Max=-0x3f3f;
for(unsigned int i=;i<arr.size();++i)//以i结尾的每个子数组
{
for(unsigned int start=;start<=i;++start)//0..i/1..i/2..i等等,前两个for找所有的子数组
{
int Eor=;
for(unsigned int k=start;k<=i;++k)//遍历每个区间:k=[start..i],求每个区间的最大异或和,arr.at(k)
Eor^=(arr.at(k));
Max=max(Max,Eor);
}
}
return Max;
} //2.记忆化优化解O(n^2)
//0...i异或结果为Sum
//0...start-1异或结果为A
//start...i的异或结果为Sum^A
int get_max_eor1(const vector<int>& arr)
{
if(arr.size()<||arr.empty())
return -; int Eor=;
int Max=-0x3f3f;
for(unsigned int i=;i<arr.size();++i)
{
Eor^=arr.at(i);//遍历0...i,Xor保存每个以i结尾的最长数组的异或和
Max=max(Max,Eor);//一:可能是整个以i结尾的数组的异或和最大
int res=;
for(unsigned int start=;start<=i;++start)
{
res^=arr.at(start);//每个以i结尾的数组的前半部分
Max=max(Max,res^Eor);//二:可能是某个子数组的异或和最大
}
}
return Max;
}
//2 O(n^2)
int get_max_eor2(const vector<int>& arr)
{
if(arr.size()<||arr.empty())
return -; vector<int> dp(arr.size(),);
int Eor=;
int Max=-0x3f3f;
for(unsigned int i=;i<arr.size();++i)
{
Eor^=arr.at(i);
Max=max(Max,Eor);
for(unsigned int start=;start<=i;++start)
{
int res=Eor^dp[start-];
Max=max(Max,res);
}
dp.at(i)=Eor;
}
return Max;
}
//3
//0...i的异或结果Eor存
//0...0异或结果 0...1的异或结果 0...2的异或结果 直到0...i-1的异或结果(装入黑盒---前缀树)
typedef struct Node
{
Node *next[];
Node()
{
next[]=nullptr;
next[]=nullptr;
}
}Node;
class Trie
{
public:
int get_max_eor3(const vector<int>& arr);
~Trie()
{
delete head;
}
private:
int max_eor(const int &num);
void add(const int &num);
static Node *head;
};
Node* Trie::head=new Node(); int Trie::get_max_eor3(const vector<int>& arr)
{
if(arr.size()<||arr.empty())
return -; int Max=-0x3f3f;
int Eor=;
Trie trie;
trie.add(); for(unsigned int i=;i<arr.size();++i)
{
Eor^=arr.at(i);
Max=max(max_eor(Eor),Max);
add(Eor);
}
return Max;
} void Trie::add(const int& num)//把num添加到前缀树中
{
Node *cur=head;
int res=;
for(int move=;move>=;--move)
{
int path=((num>>move)&);//取每一位
cur->next[path]=(cur->next[path]==nullptr?new Node():cur->next[path]);
cur=cur->next[path];
}
}
//符号位尽量为0,后面的位是0走1,1走0,没得选就将就着走,尽量保持最大化值
//每次选最优,可以找到最大值
int Trie::max_eor(const int& num)//num和前缀树中的哪个值异或和最大
{
int res=;
Node *cur=head; for(int move=;move>=;--move)
{
int path=((num>>move)&);
int best=(move==?path:(path^));
best=(cur->next[best]!=nullptr?best:(best^));
res|=((path^best)<<move);
cur=cur->next[best];
}
return res;
} int main()
{
vector<int> arr{,,,,,,};
cout<<get_max_eor(arr)<<endl;
cout<<get_max_eor1(arr)<<endl;
cout<<get_max_eor2(arr)<<endl; Trie t;
cout<<t.get_max_eor3(arr)<<endl;
return ;
}

https://blog.csdn.net/v_july_v/article/details/6685962

子数组的最大异或和---Trie的更多相关文章

  1. 算法进阶面试题07——求子数组的最大异或和(前缀树)、换钱的方法数(递归改dp最全套路解说)、纸牌博弈、机器人行走问题

    主要讲第五课的内容前缀树应用和第六课内容暴力递归改动态规划的最全步骤 第一题 给定一个数组,求子数组的最大异或和. 一个数组的异或和为,数组中所有的数异或起来的结果. 简单的前缀树应用 暴力方法: 先 ...

  2. 【js】Leetcode每日一题-子数组异或查询

    [js]Leetcode每日一题-子数组异或查询 [题目描述] 有一个正整数数组 arr,现给你一个对应的查询数组 queries,其中 queries[i] = [Li, Ri]. 对于每个查询 i ...

  3. [LeetCode] 898. Bitwise ORs of Subarrays 子数组按位或操作

    We have an array A of non-negative integers. For every (contiguous) subarray B = [A[i], A[i+1], ..., ...

  4. [LeetCode] Minimum Size Subarray Sum 最短子数组之和

    Given an array of n positive integers and a positive integer s, find the minimal length of a subarra ...

  5. 剑指Offer面试题:28.连续子数组的最大和

    一.题目:连续子数组的最大和 题目:输入一个整型数组,数组里有正数也有负数.数组中一个或连续的多个整数组成一个子数组.求所有子数组的和的最大值.要求时间复杂度为O(n).例如输入的数组为{1,-2,3 ...

  6. lintcode循环数组之连续子数组求和

    v 题目:连续子数组求和 II 给定一个整数循环数组(头尾相接),请找出一个连续的子数组,使得该子数组的和最大.输出答案时,请分别返回第一个数字和最后一个数字的值.如果多个答案,请返回其中任意一个. ...

  7. 给定一个double类型的数组arr,其中的元素可正可负可0,返回子数组累乘的最大乘积。例如arr=[-2.5,4,0,3,0.5,8,-1],子数组[3,0.5,8]累乘可以获得最大的乘积12,所以返回12。

    分析,是一个dp的题目, 设f[i]表示以i为结尾的最大值,g[i]表示以i结尾的最小值,那么 f[i+1] = max{f[i]*arr[i+1], g[i]*arr[i+1],arr[i+1]} ...

  8. 查找二维数组list[][]中的最大的子数组的和

    之前做过最大一维数组子数组的和的题目,现在将数组扩展成二维: 代码如下: #include<iostream> #define null -858993460 using namespac ...

  9. 剑指Offer:面试题31——连续子数组的最大和(java实现)

    问题描述 : 输入一个整数数组,数组里面有正数也有负数.数组中一个或连续几个整数组成一个子数组.求所有子数组的和的最大值.要求时间复杂度为O(n) 思路1:常规解法,不知道怎么描述了.. 代码: bo ...

随机推荐

  1. MySQL中case then用法

    1.查询图书价格,若价格为null,则显示unknown,若价格为10到20, 则显示10 to 20 SELECT price, CASE WHEN price='null' THEN 'UnKno ...

  2. backbone点滴

    可以查看http://www.css88.com/doc/backbone/ backbone与angular http://www.infoq.com/cn/articles/backbone-vs ...

  3. IDEA spirng boot @Autowired注解 mapper出现红色下划线解决方法

    如图所示,解决方法为: 把勾去掉即可.

  4. Fastjson-fastjson中$ref对象重复引用问题

    当你有城市数据,你需要按国内.国际.热门城市分成数组的形式给出并输出为json格式. 第一个问题,你的数据格式,需要按字母类别划分,比如: "int": { "C&quo ...

  5. Python自学:第二章 浮点数

    >>>0.1 + 0.1 0.2 >>>0.2 + 0.2 0.4 >>>2 * 0.1 0.2 >>>2 * 0.2 0.4

  6. hello2源代码解析

    String username = request.getParameter("username");/**以 String 形式返回请求参数"username" ...

  7. 管理商品demo

    1.写一个管理商品的程序# 1.商品存在文件里面# 2.添加商品的时候,商品存在的就不能添加了,数量只能是大于0的整数,价格可以是小数.整数,但是只能是大于0的# 商品名称# 商品价格# 商品数量# ...

  8. vuex-Mutation(同步)

    更改 Vuex 的 store 中的状态的唯一方法是提交 mutation.Vuex 中的 mutation 非常类似于事件: 每个 mutation 都有一个字符串的 事件类型 (type) 和 一 ...

  9. Linux中的15个基本'ls'命令示例

    ls命令是Linux中最常用的命令之一.我相信ls命令是你进入Linux 系统命令提示符时的首选命令. 我们每天都在使用ls命令,甚至常常意识不到这一点,也从没有使用所有可用的ls选项.在这篇文章,我 ...

  10. Hadoop学习笔记05_HA

    ################# HA 即 High Available 高可用.# 其作用是为了减少主从结构的单点故障,而设置备用节点,既然学习了Hadoop生态圈,那么HA配置也是必须要掌握的. ...