首页 > 其他分享 >字节笔试题-区间异或

字节笔试题-区间异或

时间:2023-11-03 12:15:30浏览次数:36  
标签:字节 int res 笔试 list range 异或 oplus

题目

给两个长度为n的数组a,b,请你计算出有多少个区间[l, r],满足 \(a_{l}\oplus a_{l+1}\oplus a_{l+2}\oplus \ldots \oplus a_{r} = b_{l}\oplus b_{l+2}\oplus b_{l+2}\oplus \ldots \oplus b_{r}\) (\(\oplus\)表示按位异或)

输入描述

第一行输入一个整数n,表示数组长度。
第二行输入n个整数\(a_i\)。
第三行输入n个整数\(b_i\)。

\[\begin{aligned}1\leq n\leq 10^{5}\\ 1\leq a_{i},bi\leq 10^9\end{aligned} \]

示例

input

5
1 2 3 4 5
5 4 3 2 1

output

3

满足条件的区间有[1,5],[2,4],[3,3]

思路

这道题如果直接暴力的话,需要两层循环来确定区间边界,确定区间之后还要对区间求异或和,这样时间复杂度就是\(O(n^3)\)

首先能想到的优化方法是,对区间进行异或和是不是可以像求和一样,使用前缀和?

异或运算满足交换律和结合律,这样通过递推可以算出[0,i)区间的异或和,其中\(0<i<=n\)

那么通过[0,i)[0,j)能否算出[i,j)呢?

其实也是可以的,因为异或运算中\(x\oplus x=0\),所以有\([i, j) = [0, i) \oplus [0,j)\)

这样就节省了求异或和的时间,时间复杂度为\(O(n^2)\)

def f1(a: list, b: list, n: int) -> int:
    s = 0
    a_s = [0] + [s := s ^ i for i in a]
    s = 0
    b_s = [0] + [s := s ^ i for i in b]

    res = 0
    for i in range(n):
        for j in range(i + 1, n + 1):
            if a_s[j] ^ a_s[i] == b_s[j] ^ b_s[i]:
                res += 1
    return res

本来以为这样够优化了,结果提交代码只能过30%,时间复杂度还是太高。最后时间快到的时候才想到怎么优化

在上面的代码中,我用a_s[j] ^ a_s[i] == b_s[j] ^ b_s[i]这个式子来判断 a b 两个数组在[i, j)区间上的异或和是否相等,其实这个式子可以变形一下,变成a_s[i] ^ b_s[i] == a_s[j] ^ b_s[j]和前面的式子是等效的

所以这个问题可以转化为:已知边界 j ,求有多少个 i ,满足a_s[i] ^ b_s[i] == a_s[j] ^ b_s[j]。这样,我们就可以用哈希表记录有多少个a_s[i] ^ b_s[i],key为a_s[i] ^ b_s[i],value为个数,为了保证 i < j 我们在计算前缀和的同时,一边算个数,一边用哈希表统计。

def f2(a: list, b: list, n: int) -> int:
    # a_s[j] ^ a_s[i] == b_s[j] ^ b_s[i] 等价为 a_s[i] ^ b_s[i] == a_s[j] ^ b_s[j]
    a_s = [0 for i in range(n + 1)]
    b_s = [0 for i in range(n + 1)]
    d = {0: 1}
    res = 0
    for i in range(n):
        a_s[i + 1] = a_s[i] ^ a[i]
        b_s[i + 1] = b_s[i] ^ b[i]
        s = a_s[i + 1] ^ b_s[i + 1]
        res += d.get(s, 0)
        d[s] = d.get(s, 0) + 1
    return res

这样时间复杂度为\(O(n)\),空间复杂度为\(O(n)\)
其实不需要用一个数组来存前缀和了,进一步优化

def f3(a: list, b: list, n: int) -> int:
    d = {0: 1}
    s = res = 0
    for i in range(n):
        s ^= a[i] ^ b[i]
        res += d.get(s, 0)
        d[s] = d.get(s, 0) + 1
    return res

为了验证算法的正确性,我写了一个程序对随机构造的数据进行验证

import random


def f1(a: list, b: list, n: int) -> int:
    s = 0
    a_s = [0] + [s := s ^ i for i in a]
    s = 0
    b_s = [0] + [s := s ^ i for i in b]

    res = 0
    for i in range(n):
        for j in range(i + 1, n + 1):
            if a_s[j] ^ a_s[i] == b_s[j] ^ b_s[i]:
                res += 1
    return res


def f2(a: list, b: list, n: int) -> int:
    # a_s[j] ^ a_s[i] == b_s[j] ^ b_s[i] 等价为 a_s[i] ^ b_s[i] == a_s[j] ^ b_s[j]
    a_s = [0 for i in range(n + 1)]
    b_s = [0 for i in range(n + 1)]
    d = {0: 1}
    res = 0
    for i in range(n):
        a_s[i + 1] = a_s[i] ^ a[i]
        b_s[i + 1] = b_s[i] ^ b[i]
        s = a_s[i + 1] ^ b_s[i + 1]
        res += d.get(s, 0)
        d[s] = d.get(s, 0) + 1
    return res


def f3(a: list, b: list, n: int) -> int:
    d = {0: 1}
    s = res = 0
    for i in range(n):
        s ^= a[i] ^ b[i]
        res += d.get(s, 0)
        d[s] = d.get(s, 0) + 1
    return res


def main():
    for i in range(100):
        n = random.randint(1, 10 ** 3)
        a = [random.randint(1, 10 ** 5) for i in range(n)]
        b = [random.randint(1, 10 ** 5) for i in range(n)]
        if f1(a, b, n) != f3(a, b, n):
            print('error')
            print(n)
            print(a)
            print(b)
            break


main()

经过验证,优化后的算法结果上和优化前的一致,说明算法是正确的

对三种方法测时,可以看到我们优化后对时间的提升是非常大的

标签:字节,int,res,笔试,list,range,异或,oplus
From: https://www.cnblogs.com/mengzhuo/p/17807331.html

相关文章

  • i++与++i字节码分析
    最近碰到几道关于i++与++i相关的题,我们从字节码角度来分析执行情况,该文章需要读者有字节码相关基础及了解方法调用机制。分析下面是一个题,请问下面代码输出什么?publicstaticvoidf(){inti=1;System.out.println(i+++i++);}答案是3。相信有小伙伴会回......
  • 实现一个极简的字节数组对象池
    .NET利用ArrayPoolPool<T>和MemoryPool<T>提供了针对Array/Memory<T>的对象池功能。最近在一个项目中需要使用到针对字节数组的对象池,由于这些池化的字节数组相当庞大,我希望将它们分配到POH上以降低GC的压力。由于ArrayPoolPool<T>没法提供支持,所以我提供了一个极简的实现。目录一......
  • javap - 查阅 Java 字节码
    javap命令可以用来查阅字节码文件,可以将指定的字节码文件反编译,反解析出当前类对应基本信息、常量池(Constantpool)、字段区域、方法区(Code[JVM指令集])、异常表(Exceptiontable)、本地变量表(LocalVariableTable)、行数表(LineNumberTable)和字节码操作数栈的映射表(StackMapTable)等信息......
  • 从零开发基于ASM字节码的Java代码混淆插件XHood
    项目背景因在公司负责基础框架的开发设计,所以针对框架源代码的保护工作比较重视,之前也加入了一系列保护措施例如自定义classloader加密保护,授权license保护等,但都是防君子不防小人,安全等级还比较低经过调研各类加密混淆措施后,决定自研混淆插件,自主可控,能够贴合实际情况进行定制......
  • 大量生成字节码导致元空间溢出问题排查
    前几天生产环境出现了一个问题,gc日志里面某一个时间段出现了大量的FullGC,而且都是回收元空间内存失败了,最终导致了JVM停止运行,微服务中的某个服务发生了宕机。下面记录下排查该问题的过程。首先我们根据服务器的CPU核心数和内存大小,设置了元空间的最大值为512M,这是前提。在服务G......
  • c# 将十进制数字转换成字节数组
    //将十进制数字转换成字节数组//由数字创建字节数组publicstaticbyte[]DecimalToByteArray(decimalsrc){//创建内存流MemoryStream,stream作为存放二进制数据的缓存using(MemoryStreamstream=newMemoryStream())......
  • 深入理解Java IO流: 包括字节流和字符流的用法、文件读写实践
    (文章目录)......
  • 数据结构与算法(LeetCode)第一节:认识复杂度,对数器,二分法与异或运算
    一、认识复杂度1.评估算法优劣的核心指标:时间复杂度:当完成了表达式的建立,只要把最高阶项留下即可。低阶项都去掉,高阶项的系数也去掉,记为O(去掉系数的高阶项);​ 时间复杂度是衡量算法流程的复杂度的一种指标,该指标只与数据量有关,与过程之外的优化无关常见的时间复杂度(从好到坏)O......
  • 溢信科技笔试
    1.选择题本次笔试一共五道选择题,其中两道都是考的continue,因此在这里记录一下continue和break的区别在Java语法中,continue是跳过本次循环,进行下一次循环;而break是直接跳出循环。 在上图中,我们会发现if里面的语句走完的时候,就立马跳出循环,当i取余不等于0的时候才......
  • 数据的存储--->【大小端字节序】(Big Endian)&(Little Endian)
    博主主页:@威化小餅干......