首页 > 其他分享 >子数组的最大异或和问题

子数组的最大异或和问题

时间:2022-12-26 22:01:00浏览次数:53  
标签:arr 最大 int max eor 异或 数组

子数组的最大异或和问题

作者:Grey

原文地址:

博客园:子数组的最大异或和问题

CSDN:子数组的最大异或和问题

题目描述

数组中所有数都异或起来的结果,叫做异或和。给定一个数组 arr,其中可能有正、有负,有零,返回 arr 的最大子数组异或和

题目链接见:牛客-子数组的最大异或和

暴力解

枚举每个子数组的异或和,抓取全局最大值返回,整个算法时间复杂度\(O(N^3)\),整个过程比较简单,不赘述,基于这个暴力解法,可以有优化一些的算法,就是利用前缀异或和数组,时间复杂度可以减少到\(O(N^2)\),思路如下

第一步

申请一个和原始数组一样长的前缀异或和数组

int[] eor = new int[arr.length];

其中eor[i]表示原始数组 0 位置到 i 位置的异或和是多少,实现代码如下:

    eor[0] = arr[0];
    for (int i = 1; i < n; i++) {
      eor[i] = eor[i - 1] ^ arr[i];
    }

有了 eor 数组以后,对于任意 i 位置,0 到 i 区间的异或和就可以直接获取到了,接下来是枚举数组中任意两个位置 i 和 j 区间的异或和,由于

i ~ j 之间的异或和等于 eor[j] ^ eor[i-1](i > 0),所以

任何两个位置之间的异或和信息可以通过如下代码求得,其中 max 是全局异或和的最大值

    for (int i = 1; i < n; i++) {
      max = Math.max(max, eor[i]);
      for (int j = i; j < n; j++) {
        max = Math.max(max, eor[j] ^ eor[i - 1]);
      }
    }

完整代码如下

  public static int maxEor1(int[] arr, int n) {
    int[] eor = new int[arr.length];
    int max = arr[0];
    eor[0] = arr[0];
    for (int i = 1; i < n; i++) {
      eor[i] = eor[i - 1] ^ arr[i];
    }
    for (int i = 1; i < n; i++) {
      max = Math.max(max, eor[i]);
      for (int j = i; j < n; j++) {
        max = Math.max(max, eor[j] ^ eor[i - 1]);
      }
    }
    return max;
  }

整个算法复杂度是\(O(N^2)\),并不是最优解。

最优解

根据上述暴力解法,时间复杂度比较高的部分是:

当确定了 0 ~ i 位置的异或和以后,如何定位 0 ~ j 这个区间,使得 j ~ i 之间的异或和最大。

暴力解法使用的是遍历的方式,而最优解,可以使用前缀树进行加速匹配,关于前缀树的内容,可以参考:前缀树的设计和实现

以数组[11,1,15,10,13,4]为例,我们把其前缀异或和数组转换成二进制,结果如下(其中eor[i..j]表示i~j的异或和)

eor[0..0] = 1011

eor[0..1] = 1010

eor[0..2] = 0101

eor[0..3] = 1111

eor[0..4] = 0010

eor[0..5] = 0110

把这些前缀异或和加入前缀树,

img

接下来,对于任何一个eor[i](0~i的异或和)来说,进入前缀树以后,前缀树需要快速找到和其匹配的eor[j],使得i~j之间的异或和最大,规则就是最高位(符号位)期待一样,紧着高位要期待不一样的值

例如:

eor[2] = 0101

eor[2] 期待和它符号位一样为0的值,紧着高位(由于前面28都是0,所以不存在和它符号不一样的,只看最后4位,

img

通过这个贪心,就可以在\(O(1)\)时间复杂度直接得到结果。

说明:如果期待遇到 0 可是前缀树没有往 0 方向的路,那直接返回 1 即可,反之亦然。

完整代码如下

  public static int maxEor(int[] arr, int n) {
    int[] eor = new int[arr.length];
    int max = 0;
    eor[0] = arr[0];
    for (int i = 1; i < n; i++) {
      eor[i] = eor[i - 1] ^ arr[i];
    }
    Trie trie = new Trie(eor);
    trie.add(eor[0]);
    for (int i = 1; i < n; i++) {
      max = Math.max(max, trie.get(eor[i]));
    }
    return max;
  }

  public static class Trie {
    public Node head;

    public Trie(int[] arr) {
      head = new Node();
      for (int eor : arr) {
        add(eor);
      }
    }

    public void add(int num) {
      Node cur = head;
      for (int bit = 31; bit >= 0; bit--) {
        int i = ((num >>> bit) & 1);
        if (cur.next[i] == null) {
          cur.next[i] = new Node();
        }
        cur = cur.next[i];
      }
    }

    public int get(int eor) {
      int expect = 0;
      Node cur = head;
      for (int bit = 31; bit >= 0; bit--) {
        // 符号位期待一样的
        // 非符号位期待相反的
        int expectBit = bit == 31 ? ((eor >>> bit) & 1) : (eor >>> bit & 1 ^ 1);
        if (cur.next[expectBit] != null) {
          expect = ((expectBit << bit) | expect);
          cur = cur.next[expectBit];
        } else {
          expectBit = (expectBit ^ 1);
          cur = cur.next[expectBit];
          expect = ((expectBit << bit) | expect);
        }
      }
      return expect ^ eor;
    }
  }

  public static class Node {
    public Node[] next = new Node[2];
  }

整个算法时间复杂度\(O(N)\),最优解。

更多

算法和数据结构笔记

标签:arr,最大,int,max,eor,异或,数组
From: https://www.cnblogs.com/greyzeng/p/17007011.html

相关文章

  • js-将数组拆分为指定长度
    场景数组:[1,2,3,4,5,6,7,8,9,10]目标:[[1,2],[3,4],[5,6],[7,8],[9,10]]思路分析借助splice方法或者slice方法,一直对数组进行指定位数的删除,并将返回的数组push到一个......
  • C语言 -- 如何传递数组参数
    一、传递普通参数,直接传入即可voidarrprint(intarr){printf("%d\n",arr);}voidmain(){intarr=123;arrprint(arr);printf("aiyou");......
  • Mysql查看连接数(连接总数、活跃数、最大并发数)
    怎么查看mysql的最大连接数showvariableslike'%max_connection%';查看最大连接数setglobalmax_connections=1000;    重新设置最大连接数怎么查看mysql的......
  • 【C++入门】(四)数组
    一. 一维数组1.1 数组的定义//数组的定义方式和变量类似。#include<iostream>#include<algorithm>usingnamespacestd;intmain(){inta[10],b[10];......
  • C++强化 | 06 一篇文章带你掌握字符数组
    导读数组是信息学中非常重要的一块内容,可以说是必备的,也几乎是信息学竞赛中写代码必用的。前面的三节课,我们讲了一维数组,让大家对一维数组有了更加全面深刻的认知。本篇文章......
  • JavaScript学习--Item30 数组进阶全掌握
    在程序语言中数组的重要性不言而喻,JavaScript中数组也是最常使用的对象之一,数组是值的有序集合,由于弱类型的原因,JavaScript中数组十分灵活、强大,不像是Java等强类型高级语......
  • 数组
    1、二分查找--704importjava.lang.annotation.Target;publicclass_704{publicstaticvoidmain(String[]args){_704test=new_704();......
  • 数组处理方法——filter
    一、作用普通记忆:filter用于对数组的过滤,返回值是一个新的数组,数组中的内容是符合条件的元素。使用记忆法记忆:谐音联想记忆+地点故事联想+地点定位记忆一......
  • 冒泡、数组逆序
    #define_CRT_SECURE_NO_WARNINGS1#include<iostream>#include<string>usingnamespacestd;//1、数组逆序//intmain()//{//intarr[5]={1,3,2,5,4};//inti=0......
  • JS手写题随笔-20221226.1 ---- 数组打平
    1.借助reduce递归functionflat(arr){if(!Array.isArray(arr)||arr.length===0){return[];}returnarr.reduce((pre,cur)=>{......