首页 > 试题广场 >

子数组的最大异或和

[编程题]子数组的最大异或和
  • 热度指数:1414 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
数组异或和的定义:把数组中所有的数异或起来得到的值。给定一个整型数组arr,其中可能有正、有负,有零,求其中子数组的最大异或和。

输入描述:
输出包含两行,第一行一个整数n,代表数组arr长度,第二个n个整数,代表数组arr


输出描述:
输出一个整数,代表其中子数组的最大异或和。
示例1

输入

4
3 -28 -29 2

输出

7

说明

{-28,-29}这个子数组的异或和为7,是所有子数组中最大的 

备注:
时间复杂度,额外空间复杂度
牛客的输入格式总是这么魔性,这题用缓冲流就数组越界,用Scanner又很慢。整体思路就是用字典树存储数组的前缀异或和,在遍历求前缀和的过程中考虑以arr[i]结尾时能够得到的最大异或和。假设能够让你得到这个最大异或和的子数组以arr[s]开头,则我们就需要确定s的位置。根据异或的性质,数组0~i范围上的异或和异或上数组0~s-1范围上的异或和,就能得到s~i范围上的异或和。而在遍历到i时,前面的前缀异或和已经全部加入到了字典树中,所以我们只要找到某个字典树中已经存在的前缀异或和,它与当前的前缀异或和eor异或完之后可以得到最大值就行了。因此我们在寻找这个符合条件的前缀异或和时,有如下两个贪心的思想:
  1. 对于符号位,我们希望找到一个与当前异或和状态相同的前缀异或和,因为同状态异或得0,可以使异或结果变成正数;
  2. 对于非符号位,我们希望找到一个与当前异或和状态不同的前缀异或和,因为不同状态才能使得异或结果为1。
从高位开始往低位执行上面的贪心策略,直到遍历完整型的32位(因为尽可能让高位为1能够使得异或和更大)。
import java.util.Scanner;

class Node {
    public Node[] next = new Node[2];     // 每个节点表示整数的一个位,有0和1两个状态
}

class NumTrie {
    public Node head = new Node();
    public void add(int num) {
        Node cur = head;
        for(int move = 31; move >= 0; move--){
            int bit = (num >> move) & 1;    // 取出从右往左的第move位
            cur.next[bit] = cur.next[bit] == null? new Node(): cur.next[bit];
            cur = cur.next[bit];
        }
    }
    
    public int maxXOR(int num) {
        Node cur = head;
        int res = 0;
        for(int move = 31; move >= 0; move--){
            int bit = (num >> move) & 1;
            int expect = move == 31? bit: bit ^ 1;      // 当前位为符号位时,希望要走的路径与当前位相同
            expect = cur.next[expect] == null? expect ^ 1: expect;      // 期望路径不空才能走
            res |= (bit ^ expect) << move;     // 把res中这一位的状态改了
            cur = cur.next[expect];
        }
        return res;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] arr = new int[n];
        int max = Integer.MIN_VALUE;
        int eor = 0;      // 前缀异或和
        NumTrie trie = new NumTrie();
        trie.add(0);
        for(int i = 0; i < n; i++) {
            arr[i] = sc.nextInt();
            eor ^= arr[i];
            max = Math.max(max, trie.maxXOR(eor));     // 找到数组以arr[i]结尾时,最大的异或和
            trie.add(eor);
        }
        System.out.println(max);
    }
}

发表于 2021-12-07 13:16:39 回复(0)