LeetCode-898.[子数组按位或操作]

3 阅读2分钟

1.题目描述

给定一个整数数组 arr,返回所有 arr 的非空子数组的不同按位或的数量。

子数组的按位或是子数组中每个整数的按位或。含有一个整数的子数组的按位或就是该整数。

子数组 是数组内连续的非空元素序列。

示例 1:

输入: arr = [0]
输出: 1
解释:
只有一个可能的结果 0 。

示例 2:

输入: arr = [1,1,2]
输出: 3
解释:
可能的子数组为 [1][1][2][1, 1][1, 2][1, 1, 2]。
产生的结果为 1,1,2,1,3,3 。
有三个唯一值,所以答案是 3 。

示例 3:

输入: arr = [1,2,4]
输出: 6
解释:
可能的结果是 1,2,3,4,6,以及 7 。

2.解题思路

核心是维护 “以当前元素结尾的子数组的按位或结果集合” ,利用其数量有限的特点降低时间复杂度(最终时间复杂度 O(n*32),即线性复杂度),步骤如下:

  1. 初始化两个集合:

    • global_set:存储所有子数组按位或的不同结果(最终统计其大小);
    • cur_set:存储以当前元素 arr[i] 结尾的所有子数组的按位或结果(最多 32 个值)。
  2. 遍历数组中的每个元素 arr[i]

    • 新建临时集合 tmp_set,先加入 arr[i](对应子数组 [arr[i]],长度为 1 的子数组);
    • 遍历 cur_set 中的每个值 val,计算 val | arr[i] 并加入 tmp_set(对应子数组 arr[j...i]j < i,即把以 arr[i-1] 结尾的子数组扩展到 arr[i]);
    • tmp_set 去重后赋值给 cur_set(避免重复值,减少计算);
    • cur_set 中的所有值加入 global_set(收集所有可能的结果)。
  3. 遍历结束后,global_set 的大小就是答案。

3.代码实现

class Solution {
public:
    int subarrayBitwiseORs(vector<int>& arr) {
        unordered_set<int> global_set;//存储所有不同的按位或结果
        unordered_set<int> cur_set;//存储以当前元素结尾的子数组的按位或结果

        for(int num:arr){
            unordered_set<int> tmp_set;
            //1.加入当前元素自身
            tmp_set.insert(num);

            //2.将上一轮的cur_set中的值与当前num按位或,加入tmp_set
            for(int val : cur_set){
                tmp_set.insert(val | num);
            }

            //3.更新cur_set为当前tmp_set
            cur_set = move(tmp_set);

            //4.将当前cur_set的所有值加入全局集合
            global_set.insert(cur_set.begin(),cur_set.end());
        }
        return global_set.size();
    }
};