LeetCode﹝异或ி﹞解码异或区间查询第k大异或值

Posted 白鳯

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode﹝异或ி﹞解码异或区间查询第k大异或值相关的知识,希望对你有一定的参考价值。

【LeetCode】﹝异或ி﹞解码异或、区间查询、第k大异或值

解码异或后的数组★

1720. 解码异或后的数组

题目】未知 整数数组 arrn 个非负整数组成。

经编码后变为长度为 n - 1 的另一个整数数组 encoded ,其中 encoded[i] = arr[i] XOR arr[i + 1] 。例如,arr = [1,0,2,1] 经编码后得到 encoded = [1,2,3]

给你编码后的数组 encoded 和原数组 arr 的第一个元素 first(arr[0])

请解码返回原数组 arr 。可以证明答案存在并且是唯一的。

提示:

  • 2 <= n <= 104
  • encoded.length == n - 1
  • 0 <= encoded[i] <= 105
  • 0 <= first <= 105

示例

输入:encoded = [1,2,3], first = 1
输出:[1,0,2,1]
解释:若 arr = [1,0,2,1] ,那么 first = 1 且 encoded = [1 XOR 0, 0 XOR 2, 2 XOR 1] = [1,2,3]
-----------------------------------------------------------------------------------------
输入:encoded = [6,2,7,3], first = 4
输出:[4,2,0,7,4]

解题思路

  • a ^ b = c (已知a和异或结果c,按顺序推)
class Solution 
    public int[] decode(int[] encoded, int first) 
        int n = encoded.length;
        int[] decoded = new int[n + 1];
        decoded[0] = first;
        for (int i = 0; i < n; i++) 
            decoded[i + 1] = decoded[i] ^ encoded[i];
        
        return decoded;
    


解码异或后的排列★★

1734. 解码异或后的排列

题目】给你一个整数数组 perm ,它是前 n 个正整数的排列,且 n 是个 奇数

它被加密成另一个长度为 n - 1的整数数组 encoded,满足 encoded[i] = perm[i] XOR perm[i + 1] 。比方说,如果perm = [1,3,2],那么 encoded = [2,1]

给你encoded数组,请你返回原始数组perm。题目保证答案存在且唯一。

提示:

  • 3 <= n < 105
  • n 是奇数。
  • encoded.length == n - 1

示例

输入:encoded = [3,1]
输出:[1,2,3]
解释:如果 perm = [1,2,3] ,那么 encoded = [1 XOR 2,2 XOR 3] = [3,1]
------------------------------------------------------------------
输入:encoded = [6,5,4,6]
输出:[2,4,1,5,3]

解题思路

注意两点:n为奇数,这些数是前n个数字的排列

详细推导如下:

a  b  c  d  e  f  g
[a^b, b^c, c^d, d^e, e^f, f^g]
---------------------------------------------------------------------------------------
(除了最后一个元素的异或值)
(a^b) ^         (c^d) ^         (e^f)         = a ^ b ^ c ^ d ^ e ^ f = except_last_xor
(所有元素的异或值)
a ^ b ^ c ^ d ^ e ^ f ^ g = all_xor
(求得最后一个元素)
all_xor ^ except_last_xor = g
这下反推即可

代码如下

class Solution 
    public int[] decode(int[] encoded) 
        int n = encoded.length + 1;
        int xorN = 0;
        for (int i = 0; i <= n; i++) 
            xorN ^= i;
        
        int partXor = 0;
        for (int i = 0; i < encoded.length; i += 2) 
            partXor ^= encoded[i];
        
        int last = xorN ^ partXor;
        int[] decoded = new int[n];
        decoded[n - 1] = last;
        for (int i = n - 2; i >= 0; i--) 
            decoded[i] = decoded[i + 1] ^ encoded[i];
        
        return decoded;
    


找出所有子集的异或总和再求和★

1863. 找出所有子集的异或总和再求和

题目】一个数组的 异或总和 定义为数组中所有元素按位 XOR 的结果;如果数组为 空 ,则异或总和为 0

  • 例如,数组 [2,5,6] 的 异或总和 为 2 XOR 5 XOR 6 = 1

    给你一个数组 nums ,请你求出 nums 中每个 子集异或总和 ,计算并返回这些值相加之

**注意:**在本题中,元素 相同 的不同子集应 多次 计数。

数组 a 是数组 b 的一个 子集 的前提条件是:从 b 删除几个(也可能不删除)元素能够得到 a

提示:

  • 1 <= nums.length <= 12
  • 1 <= nums[i] <= 20

示例

输入:nums = [1,3]
输出:6
解释:[1,3] 共有 4 个子集:
- 空子集的异或总和是 0- [1] 的异或总和为 1- [3] 的异或总和为 3- [1,3] 的异或总和为 1 XOR 3 = 20 + 1 + 3 + 2 = 6

解题思路

方法一递归枚举(同求子集法相同)

题目中要求元素相同的不同子集应多次计数,所以可以视为求不相同元素的子集

(这里有个坑,不能用求子集的回溯法,更新当前异或值时会影响到后面的结果~)

class Solution 
    int sum;
    public int subsetXORSum(int[] nums) 
        sum = 0;
        dfs(nums, 0, 0);
        return sum;
    

    private void dfs(int[] nums, int index, int curxor) 
        if (index == nums.length) 
            sum += curxor;
            return;
        
        dfs(nums, index + 1, curxor ^ nums[index]);
        dfs(nums, index + 1, curxor);
    

方法二迭代法枚举(二进制计数法)

class Solution 
    public int subsetXORSum(int[] nums) 
        int n = nums.length;
        int sum = 0;
        for (int i = 0; i < (1 << n); i++) 
            int t = 0;
            for (int j = 0; j < n; j++) 
                if ((i >> j & 1) == 1) 
                    t ^= nums[j];
                
            
            sum += t;
        
        return sum;
    

偷个懒,官方题解的二项式展开下次再看


形成两个异或相等数组的三元组数目★★

1442. 形成两个异或相等数组的三元组数目

题目】给你一个整数数组 arr

现需要从数组中取三个下标 i、jk,其中 (0 <= i < j <= k < arr.length) 。

ab定义如下:

  • a = arr[i] ^ arr[i + 1] ^ ... ^ arr[j - 1]
  • b = arr[j] ^ arr[j + 1] ^ ... ^ arr[k]

注意^ 表示 按位异或 操作。

请返回能够令a == b成立的三元组 (i, j , k) 的数目。

示例

输入:arr = [2,3,1,6,7]
输出:4
解释:满足题意的三元组分别是 (0,1,2), (0,2,2), (2,3,4) 以及 (2,4,4)
-------------------------------------------------------------
输入:arr = [1,1,1,1,1]
输出:10

解题思路

找到异或值为0的位置,因为 a ^ a = 0,所以这些区间的元素可任意组合,共有j - i种组合。

class Solution 
    public int countTriplets(int[] arr) 
        int res = 0;
        for (int i = 0; i < arr.length; i++) 
            int t = 0;
            for (int j = i; j < arr.length; j++) 
                t ^= arr[j];
                if (j > i && t == 0) 
                    res += j - i;
                
            
        
        return res;
    


子数组异或查询★★

1310. 子数组异或查询

题目】有一个正整数数组 arr,现给你一个对应的查询数组 queries,其中 queries[i] = [Li, Ri]

对于每个查询 i,请你计算从 LiRiXOR 值(即 arr[Li] xor arr[Li+1] xor ... xor arr[Ri])作为本次查询的结果。

并返回一个包含给定查询 queries 所有结果的数组。

提示

  • 1 <= arr.length <= 3 * 10^4
  • 1 <= arr[i] <= 10^9
  • 1 <= queries.length <= 3 * 10^4
  • queries[i].length == 2
  • 0 <= queries[i][0] <= queries[i][1] < arr.length

示例

输入:arr = [1,3,4,8], queries = [[0,1],[1,2],[0,3],[3,3]]
输出:[2,7,14,8] 
解释:
数组中元素的二进制表示形式是:
1 = 0001 
3 = 0011 
4 = 0100 
8 = 1000 
查询的 XOR 值为:
[0,1] = 1 xor 3 = 2 
[1,2] = 3 xor 4 = 7 
[0,3] = 1 xor 3 xor 4 xor 8 = 14 
[3,3] = 8

解题思路

前缀数组

子数组异或查询思路同数组区间和一样,区间异或和区间和都可用前缀数组

class Solution 
    public int[] xorQueries(int[] arr, int[][] queries) 
        int n = arr.length;
        int[] preXor = new int[n + 1];
        for (int i = 0; i < n; i++) 
            preXor[i + 1] = preXor[i] ^ arr[i];
        

        int m = queries.length;
        int[] res = new int[m];
        for (int i = 0; i < m; i++) 
            res[i] = preXor[queries[i][0]] ^ preXor[queries[i][1] + 1];
        
        return res;
    


数组中两个数的最大异或值★★

421. 数组中两个数的最大异或值

方法:前缀树思想

详细见往期博客【LeetCode】﹝前缀树ி﹞最长单词、键值映射、最大异或值


找出第 K 大的异或坐标值★★

1738. 找出第 K 大的异或坐标值

题目】给你一个二维矩阵 matrix 和一个整数 k ,矩阵大小为 m x n 由非负整数组成。

矩阵中坐标 (a, b) 的 值 可由对所有满足 0 <= i <= a < m 且 0 <= j <= b < n 的元素 matrix[i][j](下标从 0 开始计数)执行异或运算得到。

请你找出 matrix 的所有坐标中第 k 大的值(k 的值从 1 开始计数)。

提示:

  • m == matrix.length
  • n == matrix[i].length
  • 1 <= m, n <= 1000
  • 0 <= matrix[i][j] <= 106
  • 1 <= k <= m * n

示例

输入:matrix = [[5,2],[1,6]], k = 1
输出:7
解释:坐标 (0,1) 的值是 5 XOR 2 = 7 ,为最大的值。
-------------------------------------------------
输入:matrix = [[5,2],[1,6]], k = 2
输出:5
解释:坐标 (0,0) 的值是 5 = 5 ,为第 2 大的值。

解题思路

二维前缀异或数组+优先队列

使用二维前缀异或数组,在优先队列中加入所有异或值,出队列第k个就是。

class Solution 
    public int kthLargestValue(int[][] matrix, int k) 
        int m = matrix.length, n = matrix[0].length;
        int[][] pre = new int[m + 1][n + 1];
        PriorityQueue<Integer> queue = new PriorityQueue<>((a, b) -> 
            return b - a;
        );
        for (int i = 0; i < m; i++) 
            for (int j = 0; j < n; j++) 
                pre[i + 1][j + 1] = pre[i + 1][j] ^ pre[i][j + 1] ^ pre[i][j] ^ matrix[i][j];
                queue.offer(pre[i + 1][j + 1]);
            
        
        int cur = 0, t = -1;
        while (k-- > 0) 
            cur = queue.poll();
        
        return cur;
    


使所有区间的异或结果为零★★★

1787. 使所有区间的异或结果为零

<难呀!!!>

以上是关于LeetCode﹝异或ி﹞解码异或区间查询第k大异或值的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode﹝异或ி﹞解码异或区间查询第k大异或值

LeetCode﹝前缀和ி﹞一维二维前缀和应用

LibreOJ #114. k 大异或和

[LOJ#114]k 大异或和

LeetCode﹝前缀树ி﹞最长单词键值映射最大异或值

LeetCode 1729 解码异或后的数组[数学 异或] HERODING的LeetCode之路