题目描述
给定一个数组,求子数组的最大异或和
一个数组的异或和为数组中所有的数异或起来的结果
暴力解
暴力解怎么搞?对于每个位置上的元素都要从0位置到当前位置开始遍历一遍,那么我们得到当前位置上元素的结果的时间复杂度是O(N2)的,对于每个位置我们都要求解,所以时间复杂度是O(N3)的时间复杂度。可以通过改变方向得到时间复杂度O(N^2)的算法
示例代码:
int maxEor(vector<int> &arr)
{
if (arr.size() == 0)
{
return 0;
}
int res = 0;
for (int i = 0; i < arr.size(); i++)
{
int eor = 0;
for (int start = i; start >= 0; start--)
{
eor = eor ^ arr[start];
res = max(eor, res);
}
}
return res;
}
最优解
最优解是通过前缀树来解的,可以通过一次遍历得到结果,时间复杂度是O(N)的,在遍历的过程中,我们每遇到一个元素我们就可以通过前缀树来获得以它结尾的最大异或和是多少(定制的前缀树可以提供该功能),然后我们再将该元素加入到前缀树中(定制的前缀树可以提供该功能),遍历完成之后我们就得到了我们的结果。
// 前缀树节点结构
class Node
{
public:
Node()
{
}
// 第一个指针为0,第二个指针为1
Node *next[2]{ nullptr,nullptr };
};
class NumTrie
{
public:
NumTrie()
{
head = new Node();
}
~NumTrie()
{
destroyTrie(head);
}
// 递归销毁该结构
void destroyTrie(Node *head)
{
if (head != nullptr)
{
if (head->next[0])
{
destroyTrie(head->next[0]);
head->next[0] = nullptr;
}
if (head->next[1])
{
destroyTrie(head->next[1]);
head->next[1] = nullptr;
}
}
}
// 向结构中添加元素
void add(int num)
{
Node *cur = head;
for (int move = 31; move >= 0; move--)
{
// 获取相应bit上的值
int path = ((num >> move) & 1);
if (cur->next[path] == nullptr)
{
cur->next[path] = new Node();
}
cur = cur->next[path];
}
}
// 获取以0-i位置上的异或和得到以i位置结尾的最大异或和
int maxXor(int num)
{
Node *cur = head;
int res = 0;
for (int move = 31; move >= 0; move--)
{
int path = (num >> move) & 1;
// 期待要选择的路,如果是符号位,那么就要选择与符号位相同的那条路
int best = move == 31 ? path : (path ^ 1);
// 实际选择的路,一定会有一条路的,非0就是1
best = cur->next[best] != nullptr ? best : (best ^ 1);
res |= (path^best) << move;// 设置答案的每一位。
cur = cur->next[best];//继续往下走
}
return res;
}
private:
Node *head = nullptr;
};
int maxXorSubarray(vector<int> &arr)
{
if (arr.size() == 0)
{
return 0;
}
int maxEor = INT32_MIN;
int eor = 0;
NumTrie nt;
nt.add(0); //很重要,否则将会出错
for (int i = 0; i < arr.size(); i++)
{
eor ^= arr[i];
maxEor = max(maxEor, nt.maxXor(eor));
nt.add(eor);
}
return maxEor;
}