前缀树+贪心 子数组最大异或和

102 阅读1分钟

题目

给定一个数组,返回数组中子数组的最大异或和

  • 遍历模型为遍历每一个子数组,通过预处理得到每一个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;
}