第一题
给定一个数组,求子数组的最大异或和。
一个数组的异或和为,数组中所有的数异或起来的结果。
简单的前缀树应用
暴力方法:

先计算必须以i结尾的子数组的异或和,然后再计算机i+1的,以此类推...
最暴力的解

public static int getMaxEor1(int[] nums) {
int maxEor = Integer.MAX_VALUE;
for (int i = 0; i < nums.length; i++) {
for (int start = 0; start <= i; start++) {
int curEor = 0;
for (int k = start; k <= start; k++) {
curEor ^= nums[k];
}
Math.max(maxEor, curEor);
}
}
return maxEor;
}

怎么优化?
异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1),这些法则与加法是相同的,只是不带进位,所以异或常被认作不进位加法。
0~i = eor
0~start-1 = eor2
strart~i = eor^eor2
例如
1 0 0 1 1 1 0 1 1 eor
1 0 0 1 1 0 0 0 0 eor2
0 0 0 0 0 1 0 1 1
优化方案,准备一个dp辅助数组记录结果。(增加空间)

//记忆化搜索优化(利用之前的计算结果)
public static int getMaxEor2(int[] nums) {
int maxEor = Integer.MAX_VALUE;
int[] dp = new int[nums.length];
int eor = 0;
for (int i = 0; i < nums.length; i++) {
eor ^= nums[i];
Math.max(maxEor,eor);
for (int start = 0; start <= i; start++) {
int curEor = eor ^ dp[start - 1];
Math.max(maxEor,curEor);
}
dp[i] = eor;
}
return maxEor;
}

结论:

O(n)的方法
思路:求以i结尾,异或和最大的子数组。0~i、1~i、2~i全部计算出来得到结果,效率很低。
黑盒直到i,里面存了0~0、0~1、0~2...0~i-1的结果,0~i的结果在eor时刻更新的,i希望黑盒可以告诉他,这里面哪个值和eor异或出来最大,那就是答案。
例如eor和0~3异或和是最大的,那么以i结尾的,4~i就是最大的。

黑盒可以告诉你,0~start ^ eor(0~i) 是最大的,就能得出start^i是最大的。
黑盒用前缀树做,以4位二进制举例,假设0~0、0~1、0~2的值,分别加入到前缀树中
假设求以3结尾情况下,最大异或和,0~3异或结果为0010,我特别希望异或后符号位还是0,后面的尽量1,所以就在前缀树里面寻找适合的路。

符号位尽量为0,后面的位是0走1,1走0,没得选就将就着走,尽量保持最大化值。
按前缀树的走法,每次选最优,可以找到最大值。
符号位为1的情况下,求补码,取反再加一
例如:
1 1 1 1
1 0 0 0 + 1
-1
1 0 1 1
1 1 0 0 + 1
-5
当符号位是1的时候,希望选1的路,让其1^1变成0,除了符号位选择的路有讲究之外,不管正负,接下来的选择是一样的,后面的位尽量都变成1。
所以选择符号位的时候,希望是和符号位的值是一样的,1选1,0选0

public static class Node {//前缀树节点
public Node[] nexts = new Node[2];//只有两个路,0/1
}
public static class NumTrie {//前缀树
public Node head = new Node();
public void add(int num) {
Node cur = head;
//位移,整数是31位
for (int move = 31; move >= 0; move--) {
//提取出每个进制里面的数字
//例如:0101 >> 3 = 0
//在和1进行与运算
//0 0 0 0
//0 0 0 1
//0 0 0 0 //取出了第一位为0
int path = ((num >> move) & 1);
//查看是否有路,没有就新建
cur.nexts[path] = cur.nexts[path] == null ? new Node() : cur.nexts[path];
cur = cur.nexts[path];
}
}
//num 0~i eor结果,选出最优再返回
public int maxXor(int num) {
Node cur = head;
int res = 0;
for (int move = 31; move >= 0; move--) {
int path = (num >> move) & 1;
//如果考察符号位希望和path是一样的 1^1=0 0^0=0
//其他位置,希望是相反的 1^0=1 0^1=1
int best = move == 31 ? path : (path ^ 1);//期待
best = cur.nexts[best] !=

这篇博客主要讲解了如何利用前缀树优化求解子数组最大异或和问题,以及通过动态规划解决换钱的方法数问题,并提供了类似题目如纸牌博弈和机器人行走问题的解题思路,旨在帮助提升编程面试中的算法能力。
最低0.47元/天 解锁文章
5万+

被折叠的 条评论
为什么被折叠?



