题目
给定一个数组,返回数组中子数组的最大异或和
- 遍历模型为遍历每一个子数组,通过预处理得到每一个0-i位置结尾的异或和
- 根据异或和性质可以求出,arr[j,i]异或和=arr[0,i]异或和 ^ arr[0,j-1]异或和
function process(arr) {
let res = 0;
let preSum = [];
preSum[0] = arr[0];
// 预处理,求出[0,i]位置的异或和
for (let i = 1; i < arr.length; i++) {
preSum[i] = arr[i] ^ preSum[i - 1];
}
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j <= i; j++) {
// arr[0,i]异或和=arr[0,j-1]异或和 ^ arr[j,i]异或和
// 根据异或和性质可以求出,arr[j,i]异或和=arr[0,i]异或和 ^ arr[0,j-1]异或和
// 求 arr[j,i]异或和
let sum = 0;
if (j === 0) {
sum = preSum[i];
} else {
sum = preSum[i] ^ preSum[j - 1];
}
res = Math.max(res, sum);
}
}
return res;
}
优化2:
- 因为不知道[0,i]的异或和与[0,i-1]范围上的哪一个子数组的异或和累加最大,所以需要每次遍历
- 但通过贪心方案
- 将异或和转换成二进制,也就是32位的整数,比如 0100,那么从高位开始,要让最后的异或和最大,0期望遇到1,1期望遇到0,注意如果存在负数,那最高位为符号位,也就是说最高位期望0遇到0,1遇到1,其余位置遇到相反情况
- 通过前缀树记录[0,i-1]范围上的子数组的异或和,每次根据[0,i]位置的异或和去选择最优路径,如果没有该最优记录就延续当前选择
class Node {
nexts = [];
}
// 前缀树
class PreTree {
head = new Node();
add(num) {
let cur = this.head;
for (let i = 31; i >= 0; i--) {
// 拿到每一位的状态 0就是0, 1就是1
let path = (num >> move) & 1;
cur.nexts[path] = cur.nexts[path] ? cur.nexts[path] : new Node();
cur = cur.nexts[path];
}
}
maxXor(sum) {
let cur = this.head;
let res = 0;
for (let i = 31; i >= 0; i++) {
let path = (sum >> i) & 1;
// 符号位相等才最优 其余位置相反才最优
let best = i == 31 ? path : path ^ 1;
// 遍历最优路径但不一定有该记录
best = cur.nexts[best] ? best : bast ^ 1;
// 设置 i 位置结果(32、31、30、29...)
res |= (path ^ best) << move;
cur = cur.nexts[best];
}
return res;
}
}
function process(arr) {
let max = -Infinity,
sum = 0;
const preTree = new PreTree();
// 先添加 0 打底
preTree.add(0);
for (let i = 0; i < arr.length; i++) {
sum ^= arr[i];
// 求 0-i 范围内最大子数组异或和 [j,i]的异或和=[0,j-1]的异或和 ^ [0,i]的异或和
max = Math.max(max, preTree.maxXor(sum));
preTree.add(sum);
}
return max;
}