算法要求

给你一个下标从 0 开始长度为 n 的整数数组 nums 和一个整数 k 。每一次操作中,你可以选择一个数并将它乘 2 。

你最多可以进行 k 次操作,请你返回 nums[0] | nums[1] | ... | nums[n - 1] 的最大值。

a | b 表示两个整数 a 和 b 的 按位或 运算。

示例 1:

输入:nums = [12,9], k = 1
输出:30
解释:如果我们对下标为 1 的元素进行操作,新的数组为 [12,18] 。此时得到最优答案为 12 和 18 的按位或运算的结果,也就是 30 。

示例 2:

输入:nums = [8,1,2], k = 2
输出:35
解释:如果我们对下标 0 处的元素进行操作,得到新数组 [32,1,2] 。此时得到最优答案为 32|1|2 = 35 。

提示:

  • 1 <= nums.length <= 105

  • 1 <= nums[i] <= 109

  • 1 <= k <= 15

算法解析

方法一:前后缀分解

核心思路

  • 要让最终的或值最大,应尽可能让某个元素通过左移k次(即乘以2^k)来增加其二进制长度。例如,将x左移k次得到x << k,其二进制长度增加k位。

关键步骤

  1. 预处理后缀或数组
    suf[i]表示数组nums[i+1..n-1]的或值。这样,当处理nums[i]时,右边元素的或值可以直接通过suf[i]获取。

  2. 遍历计算前缀或
    维护一个变量pre,表示当前遍历到的元素左边所有元素的或值。对于每个元素nums[i],计算pre | (nums[i] << k) | suf[i],并更新pre

示例代码

class Solution:
    def maximumOr(self, nums: List[int], k: int) -> int:
        n = len(nums)
        suf = [0] * n
        for i in range(n-2, -1, -1):
            suf[i] = suf[i+1] | nums[i+1]
        
        ans = pre = 0
        for x, suf_or in zip(nums, suf):
            ans = max(ans, pre | (x << k) | suf_or)
            pre |= x
        return ans

方法二:空间优化

核心思路

利用以下数学观察:

  1. 所有元素的或值allOr:直接计算数组中所有元素的或值。

  2. 固定位fixed:记录那些在多个元素中出现的位。这些位在去掉任意一个元素后,仍会保留在或值中。

关键步骤

  1. 计算allOrfixed

    • allOr:所有元素的或值。

    • fixed:遍历每个元素时,将当前allOr与当前元素的按位与结果累加到fixed中。这样,fixed保存了至少在两个元素中出现的位。

  2. 遍历每个元素
    对于每个元素x,计算(allOr ^ x) | fixed | (x << k),其中:

    • allOr ^ x:去掉x后的或值(可能错误地清除某些位)。

    • fixed:修正被错误清除的位(这些位至少在两个元素中出现)。

    • x << k:将x左移k位后的结果。

示例代码

class Solution:
    def maximumOr(self, nums: List[int], k: int) -> int:
        all_or = fixed = 0
        for x in nums:
            fixed |= all_or & x  # 记录至少出现两次的位
            all_or |= x          # 所有元素的或值
        
        # 计算每个元素左移k后的最大或值
        return max((all_or ^ x) | fixed | (x << k) for x in nums)

复杂度分析

  • 时间复杂度:两种方法均为 O (n),其中 n 为数组长度。

  • 空间复杂度

    • 方法一:O (n) (需要存储后缀或数组)。

    • 方法二:O (n) (仅使用常数空间)。

关键概念解释

  1. 或操作的性质
    或操作的结果在二进制中,只要某一位有一个 1,结果就是 1。因此,左移k次可以显著增加数值的二进制长度,从而提升或值。

  2. 固定位fixed的作用
    例如,若多个元素在某一位上为 1,则去掉任意一个元素后,该位仍为 1。fixed记录这些位,确保在计算时不会错误地清除它们。

  3. 异或操作的作用
    allOr ^ x的作用是 “假设” 去掉x后的或值,但可能错误地清除某些位。fixed修正这些错误,最终得到正确的或值。

算法总结

  • 方法一通过预处理前后缀或数组,快速计算每个元素左移后的或值。

  • 方法二通过数学优化,避免存储前后缀数组,空间效率更高。

  • 两种方法均通过贪心策略,选择最优的元素进行左移操作,确保最终结果最大。