前缀树

用途:自动补全,拼音检查,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. android外包公司—技术分享:Android开发环境搭建(长年承接安卓应用外包)

    Android开发环境搭建 1.安装JDK 1.1.由于Android是基于java语言的.所以在开发过程中,首先要做的事儿就是安装JDK. 1.2.JDK的安装步骤: 设置环境变量:我的电脑---- ...

  2. Intellij IDEA 最新旗舰版注册激活破解(2018亲测,可用)

    1.2017年亲测 参考:https://www.haxotron.com/jetbrains-intellij-idea-crack-123/ 安装IntelliJ IDEA 最新版 启动Intel ...

  3. 通过cookie记录,设置页面访问的跳转页

    通过cookie记录,设置页面访问的跳转页 转载自:http://blog.csdn.net/yixiao_naihe/article/details/26679515. 目的: 1.访问fm.htm ...

  4. 分布式监控系统(类zabbix)

    目录: 为什么要做监控? 监控系统业务需求分析: 监控系统架构设计: 监控系统表结构设计: 一.为什么要做监控系统? 市面上已经有很多成熟的监控系统,例如zabbix.nagios,为什么自己开发监控 ...

  5. ubuntu16.04中设置python3

    执行: sudo update-alternatives --install /usr/bin/python python /usr/bin/python2 100 sudo update-alter ...

  6. webstorm keys

    {"licenseId":"ThisCrackLicenseId","licenseeName":"xuza",&quo ...

  7. 笨办法29IF语句

    people = 20 cats = 30 dogs = 15 if people < cats: print "Too many cats! The world is doomed! ...

  8. 深入剖析Java中的装箱和拆箱(缓存池技术)

    以下是本文的目录大纲: 一.什么是装箱?什么是拆箱? 简单一点说,装箱就是  自动将基本数据类型转换为包装器类型:拆箱就是  自动将包装器类型转换为基本数据类型. 二.装箱和拆箱是如何实现的 1:反编 ...

  9. 8.5 C++输入相关的成员函数

    参考:http://www.weixueyuan.net/view/6411.html 总结: 输入相关的常用的类成员函数. 1) get函数有三种重载形式:不带参数.带一个参数和带有三个参数. 不带 ...

  10. mkpasswd命令

    全称mkpasswd make password(用来生成密码的一个工具):随机生成一些高强度的密码,默认生成9位由大小写字母,特殊符号和数字的密码 [root@master ~]# yum inst ...