小R的子数组权值问题题解 | 豆包MarsCode AI刷题

56 阅读4分钟

小R的子数组权值

问题描述

小R有一个长度为 n 的数组 a,她定义每个子区间 [l, r] 的权值为 a[l] | a[l+1] | ... | a[r],即该区间内所有元素的按位或运算结果。小R非常好奇,在这 n × (n + 1) / 2 个子区间中,究竟有多少种不同的权值。
她希望你能帮她计算一下,所有子区间中的不同权值总共有多少种。


测试样例

样例1:

输入:a = [1, 2, 4]
输出:6

样例2:

输入:a = [5, 3, 8, 1]
输出:8

样例3:

输入:a = [1, 1]
输出:1

样例4:

输入:a = [7, 8, 9, 10, 11]
输出:6

要解决这个问题,我们的目标是计算数组中所有子区间的不同权值的个数。每个子区间的权值是该区间内所有元素的按位“或”运算结果(即 | 运算)。对于所有的子区间,求出它们的“或”值,并统计不同的“或”值的数量。

分析思路

  1. 子区间定义

    • 数组 a 的所有子区间 [l, r],其中 1 <= l <= r <= n。这意味着有 n * (n + 1) / 2 个子区间。
  2. 子区间的“或”运算

    • 对于每一个子区间 [l, r],我们需要计算它们的元素的按位“或”结果。按位“或”运算的性质是:当我们将新的元素加入到当前的“或”结果中时,结果只会增大或者保持不变(不会减小)。
  3. 优化方向

    • 通过对每个子区间的右端点 r(从 1 到 n)依次扩展,保持当前子区间的“或”值。如果当前子区间 [l, r] 的“或”值已经存在,那么我们就不再重复计算。
    • 使用集合来存储已经出现过的不同“或”值,因为集合具有去重功能。
  4. 关键操作

    • 对于每个可能的起始位置 l,我们从 r = l 开始扩展,计算每个子区间 [l, r] 的“或”值,并将其加入到一个集合中。

代码实现

def solution(a):
    # 用集合存储所有不同的"或"值
    or_values = set()
    
    # 遍历每个子区间的起始位置
    for i in range(len(a)):
        current_or = 0  # 当前子区间的"或"值
        # 从i开始,扩展到所有可能的右端点
        for j in range(i, len(a)):
            current_or |= a[j]  # 对子区间[a[i], a[j]]进行"或"运算
            or_values.add(current_or)  # 将结果加入集合中
    
    # 返回集合中不同"或"值的个数
    return len(or_values)

if __name__ == '__main__':
    # 测试用例
    print(solution([1, 2, 4]) == 6)  
    print(solution([5, 3, 8, 1]) == 8)  
    print(solution([1, 1]) == 1)  
    print(solution([7, 8, 9, 10, 11]) == 6)  

解释:

  1. 外层循环

    • 对于每个起始位置 i,我们计算从位置 i 开始的所有子区间。
  2. 内层循环

    • 对于每个 i,我们逐步扩展右端点 j,计算当前子区间 [i, j] 的“或”运算结果。
  3. 集合存储

    • 我们将每次计算得到的“或”值加入到集合 or_values 中,集合会自动去重,从而确保只统计不同的“或”值。
  4. 最终结果

    • 最终返回集合 or_values 的大小,即不同“或”值的个数。

复杂度分析:

  • 时间复杂度

    • 外层循环遍历所有的起始位置 i,内层循环遍历每个起始位置的所有可能的右端点 j。因此,时间复杂度为 O(n^2),其中 n 是数组的长度。
  • 空间复杂度

    • 我们使用一个集合来存储不同的“或”值。最坏情况下,集合的大小可能接近 n * (n + 1) / 2,即所有子区间的数量。因此,空间复杂度为 O(n^2)。

总结

  1. 按位“或”运算:通过“或”运算可以理解为将两个数的二进制位进行合并,结果的每一位要么为 1,要么为 0,且当某一位有 1 时,合并后的结果该位也为 1。这个性质使得“或”运算结果不会减少。
  2. 子区间扩展:通过遍历每个可能的起始位置 i 和结束位置 j,逐步计算每个子区间的“或”值,并存储到集合中。集合的去重特性确保了最终统计的是不同的“或”值。
  3. 优化策略:对于每个起始位置 i,内层循环逐步扩展右端点 j,避免了重复计算,因此可以更高效地处理。

启发

  1. 数据结构的应用:集合用于去重是一个重要的优化点,它避免了我们手动检查重复的操作,使得问题更加简洁高效。
  2. 按位运算的特性:理解按位“或”的性质可以帮助更好地优化算法。例如,按位“或”运算的结果不会减小,意味着我们只需要关注如何逐步扩展子区间,而无需回溯或重新计算。
  3. 双重循环的优化:虽然时间复杂度为 O(n²),但通过集合的去重和按位运算的特性,实际的运行时间可能远低于最坏情况。实际应用时可以尝试进一步优化或用更高效的算法处理。