898. 子数组按位或操作

我们有一个非负整数数组 A

对于每个(连续的)子数组 B = [A[i], A[i+1], ..., A[j]] ( i <= j),我们对 B 中的每个元素进行按位或操作,获得结果 A[i] | A[i+1] | ... | A[j]

返回可能结果的数量。 (多次出现的结果在最终答案中仅计算一次。)

 

示例 1:

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

示例 2:

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

示例 3:

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

提示:

  1. 1 <= A.length <= 50000
  2. 0 <= A[i] <= 10^9

本解答借鉴了https://blog.csdn.net/qq_20633793/article/details/82314599 这篇文章,若答主介意则给删除。

本文在对原文进行了部分补充:

解答思路如下:

class Solution {
public:
    int subarrayBitwiseORs(vector<int>& A) {
        //方法1,时间复杂度为O(n^2)
        // int num=A.size();
        // unordered_set<long> ans;
        // for(int i=0;i<num;i++){
        //     long res=0;
        //     for(int j=1;j<=num-i;j++){
        //         res=A[i+j-1]|res;
        //         ans.insert(res);
        //     }
        // }
        // return ans.size();
        
        //本题关键为去重,并选用Set的数据结构用来查找,去重
        
        //假设输入为[1,4,2,8,16]
        //这里的cur2 ,是动态规划的体现
        unordered_set<int> res, cur, cur2;
        //循环n次,
        for (int i: A) {
            cur2 = {i};
          //i=8时,cur代表的是上一轮的cur2 ,为1|4|2, 4|2, 2  ,并利用set的特性将其中的冗余元素进行了去除
            //最坏情况下,在第n圈中,循环n-1次
            for (int j: cur) cur2.insert(i|j);
            //处理后,cur2代表的是1|4|2|8, 4|2|8, 2|8, 8 并利用set的特性将其中的冗余元素进行了去除
            cur = cur2;
            
            for (int j: cur) res.insert(j);    //对新结果进行了插入
        }
        return res.size();
    }
};

如下图:

方法1采用,先排除当前位,如对于2,则从A到Z方向,每一个数都利用了上一"横"层的结果,在 纵向上体现了动态规划的思想,但这里无法进行优化,只能有多少行,就内层就循环多少次,而从8到0,这里也可以采用动态规划的思想,对于每个数,都可以利用上一"竖"列的结果,而这一竖列,每一格代表其本身及其右侧进行或运算累计结果,这一数列存在大量冗余的数据,采用Set的数据结构,去除了冗余,大大地降低了时间复杂度。

为了确定一个数组是否可以根据给定的条件划分成符合条件的数组,我们需要遍历整个数组并检查每个可能的组合。以下是一个简单的算法: 1. 首先,创建三个计数器,分别记录2个、3个相同元素和3个连续递增序列(差值为1)的数量。 2. 对于数组中的每个元素,执行以下操作: - 如果当前元素与前一个元素不同,且之前有至少两个相同的元素(计数器`count_two`),则说明找到了一个符合2个相同元素的数组,更新计数器并将它们复。 - 同理,如果当前元素与前两个元素相同(计数器`count_three_same`),但与前一个元素不同,表示找到了一个符合3个相同元素的数组。 - 如果当前元素比前一个大1(并且`count_increasing`已经计算了3次),那么我们找到了一个符合3个递增序列的数组,更新计数器并将它们复。 3. 遍历结束后,检查计数器。若所有计数器都为0,则无法找到符合要求的数组;否则,存在至少一个数组满足条件。 下面是伪代码形式的算法描述: ```c int count_two = 0, count_three_same = 0, count_increasing = 0; for (int i = 1; i < array_length; i++) { if (array[i] == array[i - 1]) { count_two++; if (i >= 2 && array[i] == array[i - 2]) { count_three_same++; } // 判断连续递增 if (count_increasing == 2 && array[i] == array[i - 1] + 1) { count_increasing = 0; } else if (array[i] > array[i - 1]) { count_increasing++; } } else { // 更新计数器 if (count_two > 0) { // ... } count_two = 0; // ... } } // 检查最后的状态,决定是否能划分 if (count_two > 0 || count_three_same > 0 || count_increasing > 0) { // 可以划分 } else { // 不可以划分 } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值