左神算法笔记(九)——前缀树扩充

题目一

给定一个数组,求子数组的最大异或和。一个数组的异或和为数组中所有的数异或起来的结果。

思路

之前做个一个找到异或和为0的最长子数组,思路是将每一个节点当做异或和的最后一个节点,求解最长的子数组。同样这个题目可以以这种方法解决。以i位置结尾的最大异或和的值,最大的异或和一定包含在其中。
暴力解法就是从0-i,1-i,2-i。。。进行遍历,求解出以i为末尾的最大异或和的值,不断遍历,求出最后的结果。

精简思路:
准备辅助数组dp,每一个位置在算完之后结果都保存在数组中,将当前arr【i】和0-start-1的异或结果异或,此时可以少一遍循环,就可以计算出start到i的异或结果。

前缀树思路:
将每一次从0到i异或的结果的二进制序列扔到前缀树中,形成一颗前缀树,当新的i出现,此时希望将异或结果求最大,则首位还是希望为0,此时符号位为正数。
首位确定好之后,则下面的路希望尽量选1,此时数值尽量大,所以数据位需要与0-i异或结果尽量相反,求解最大。
其实前缀树思路是在第二种思路上进行的改进,利用两个前k项异或求出最大的异或结果

代码

暴力解代码

public static int getMaxE(int[] arr){
	int max = Integer.MIN_VALUE;
	for(int i =0;i<arr.length;i++){
		for(int start = 0;start <=i,start++){
			int res = 0;
			for(int k = start;k<i;k++){
				res ^= arr[k];
			}
			max = Math.max(max,res);
		}
	}
	return max;
}

public static int getMaxE2(int[] arr){
	int max = Integer.MIN_VALUE;
	int[] dp = new int[arr.length];
	int eor = 0;
	for(int i = 0;i<arr.length;i++){
		eor ^= arr[i];
		for(int start = 1;start <= i;start ++){
			//eor保存的是从0到i的异或结果,此时再异或上从0到start-1的异或结果,就是从start到i的异或结果。
			int curEor = eor ^ dp[start-1];
			max =Math.max(max,curEor);
		}
		dp[i] = eor;
	}
	return max;

前缀树:

public static class Node{
	public Node[] nexts = new Nodes[2];
}
public static class NumTrie{
	public Node head = new Node();
	public void add(int num){
		Node cur = head;
		//将node中的每一位都变成二进制的形式
		for(int move = 31;move >= 0;move --){
			int path = ((num >> move)&1);
			cur.next[path] = cur.nexts[path] == null ? new Node() :cur.nexts[path];
			cur = cur.nexts[path];
		}
	}
	public int maxXor(int num){
		Node cur = head;
		int res = 0;
		//有了当前值则希望找到与之匹配可以形成最大的值,首位希望可以保持一致,就会让数变正,其他位置则希望不一样,则会尽量让数据位变1
		for(int move = 31;move >= 0;move--){
			int path = (num >> move) &1;
			int best = move == 31 ? path :(path ^1);
			best = cur.nexts[best] != null ? best : (best ^1);
			res |= (path^best) << move;
			cur = cur.nexts[best];
		}
		return res;
	}
}

public static int maxXorSubarray(int[] arr){
	if(arr == null || arr.length == 0){
		return 0;
	}
	int max = Integer.MIN_VALUE;
	int eor = 0;
	NumTrie numTrie = new NumTrie();//此时为一个黑盒
	numTrie.add(0);
	for(int i = 0; i<arr.length;i++){
		eor ^= arr[i];
		max = Math.max(max,numTrie.maxXor(eor));//求解现在的最大值
		numTrie.add(eor);//在前缀树中增加当前0-i异或结果
	}
	return max;
}

你可能感兴趣的:(左神算法专栏)