子数组的最大异或和---Trie
前缀树
用途:自动补全,拼音检查,ip路由(最长前缀匹配),九宫格打字预测
还有其他的数据结构,如平衡树和哈希表,使我们能够在字符串数据集中搜索单词。为什么我们还需要 Trie 树呢?尽管哈希表可以在 O(1)O(1) 时间内寻找键值,却无法高效的完成以下操作:
- 找到具有同一前缀的全部键值。
- 按词典序枚举字符串的数据集。
Trie 树优于哈希表的另一个理由是,随着哈希表大小增加,会出现大量的冲突,时间复杂度可能增加到 O(n)O(n),其中 nn 是插入的键的数量。与哈希表相比,Trie 树在存储多个具有相同前缀的键时可以使用较少的空间。此时 Trie 树只需要 O(m)O(m) 的时间复杂度,其中 mm 为键长。而在平衡树中查找键值需要 O(m \log n)O(mlogn) 时间复杂度。
Trie 树是一个有根的树,其结点具有以下字段:。
- 最多 RR 个指向子结点的链接,其中每个链接对应字母表数据集中的一个字母。
- 本文中假定 RR 为 26,小写拉丁字母的数量。
- 布尔字段,以指定节点是对应键的结尾还是只是键前缀。
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的更多相关文章
- 算法进阶面试题07——求子数组的最大异或和(前缀树)、换钱的方法数(递归改dp最全套路解说)、纸牌博弈、机器人行走问题
主要讲第五课的内容前缀树应用和第六课内容暴力递归改动态规划的最全步骤 第一题 给定一个数组,求子数组的最大异或和. 一个数组的异或和为,数组中所有的数异或起来的结果. 简单的前缀树应用 暴力方法: 先 ...
- 【js】Leetcode每日一题-子数组异或查询
[js]Leetcode每日一题-子数组异或查询 [题目描述] 有一个正整数数组 arr,现给你一个对应的查询数组 queries,其中 queries[i] = [Li, Ri]. 对于每个查询 i ...
- [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], ..., ...
- [LeetCode] Minimum Size Subarray Sum 最短子数组之和
Given an array of n positive integers and a positive integer s, find the minimal length of a subarra ...
- 剑指Offer面试题:28.连续子数组的最大和
一.题目:连续子数组的最大和 题目:输入一个整型数组,数组里有正数也有负数.数组中一个或连续的多个整数组成一个子数组.求所有子数组的和的最大值.要求时间复杂度为O(n).例如输入的数组为{1,-2,3 ...
- lintcode循环数组之连续子数组求和
v 题目:连续子数组求和 II 给定一个整数循环数组(头尾相接),请找出一个连续的子数组,使得该子数组的和最大.输出答案时,请分别返回第一个数字和最后一个数字的值.如果多个答案,请返回其中任意一个. ...
- 给定一个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]} ...
- 查找二维数组list[][]中的最大的子数组的和
之前做过最大一维数组子数组的和的题目,现在将数组扩展成二维: 代码如下: #include<iostream> #define null -858993460 using namespac ...
- 剑指Offer:面试题31——连续子数组的最大和(java实现)
问题描述 : 输入一个整数数组,数组里面有正数也有负数.数组中一个或连续几个整数组成一个子数组.求所有子数组的和的最大值.要求时间复杂度为O(n) 思路1:常规解法,不知道怎么描述了.. 代码: bo ...
随机推荐
- python requests用法总结
requests是一个很实用的Python HTTP客户端库,编写爬虫和测试服务器响应数据时经常会用到.可以说,Requests 完全满足如今网络的需求 本文全部来源于官方文档 http://docs ...
- POI 导入excel 代码记录 方便以后粘贴
import java.io.FileInputStream; import java.io.InputStream; import javax.annotation.Resource; import ...
- 最长公共前缀(java实现)
题目: 编写一个函数来查找字符串数组中的最长公共前缀. 如果不存在公共前缀,返回空字符串 "". 示例 1: 输入: ["flower","flow& ...
- 第 8 章 容器网络 - 069 - Calico 的默认连通性
相同calico 网络之间的连通性 测试一下 bbox1 与 bbox2 的连通性: ping 成功,数据包流向如下图所示. 1)根据 bbox1 的路由表,将数据包从 cal0 发出. 2)数据经过 ...
- Spring @RequestParam、@RequestBody和@ModelAttribute区别
一.@RequestParamGET和POST请求传的参数会自动转换赋值到@RequestParam 所注解的变量上1. @RequestParam(org.springframework.web.b ...
- RabbitMQ(2) 一般介绍
RabbitMQ 即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用. 消息中间件在互联网公司的使用中越来越多,刚才还看到新闻阿里将RocketMQ捐献给了apa ...
- 553. Optimal Division
题目: Given a list of positive integers, the adjacent integers will perform the float division. For ex ...
- Loadrunner加密算法脚本与token作为get请求url上的参数处理
1.当字符串被封装好加密时(下例将算法封装在md5中),使用Loadrunner编写脚本,需要进行如下操作: 1)将md5.h文件添加到Extra Files 下,如图(Loadrunne ...
- 2D过渡模块的其他属性
官网上关于过渡属性的值: 属性 描述 CSS transition 简写属性,用于在一个属性中设置四个过渡属性. 3 transition-property 规定应用过渡的 CSS 属性的名称. 3 ...
- vue bus的使用
vue中的bus事件,一般作为中央事件总线来使用 简单例子:比如在A,B组件为兄弟组件,现在A要调用B的中C事件 1.创建一个bus.js 内容: import Vue from 'vue' cons ...