按位与最大的最长子数组

标签: 位运算 脑筋急转弯 数组

难度: Medium

给你一个长度为 n 的整数数组 nums

考虑 nums 中进行 按位与(bitwise AND)运算得到的值 最大非空 子数组。

  • 换句话说,令 knums 任意 子数组执行按位与运算所能得到的最大值。那么,只需要考虑那些执行一次按位与运算后等于 k 的子数组。

返回满足要求的 最长 子数组的长度。

数组的按位与就是对数组中的所有数字进行按位与运算。

子数组 是数组中的一个连续元素序列。

示例 1:

输入:nums = [1,2,3,3,2,2]
输出:2
解释:
子数组按位与运算的最大值是 3 。
能得到此结果的最长子数组是 [3,3],所以返回 2 。

示例 2:

输入:nums = [1,2,3,4]
输出:1
解释:
子数组按位与运算的最大值是 4 。 
能得到此结果的最长子数组是 [4],所以返回 1 。

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 106

Submission

运行时间: 55 ms

内存: 28.4 MB

class Solution:
    def longestSubarray(self, nums: List[int]) -> int:
        maxVal = max(nums)
        cnt = 0
        n = len(nums)
        i = 0
        while i < n:
            if nums[i] != maxVal:
                i += 1
            else:
                L = i
                while i < n and nums[i] == maxVal:
                    i += 1
                cnt = max(cnt, i - L)
        return cnt

Explain

此题解的思路首先是找到数组中的最大值maxVal,因为只有包含最大值的子数组才可能得到最大的按位与结果。接着,遍历数组,寻找包含最大值maxVal的最长连续子数组。通过设置一个计数器cnt来记录最长的连续子数组的长度。当遇到与maxVal相等的元素时,开始计数,直到遇到不等的元素为止,每次都更新最大的连续长度cnt。

时间复杂度: O(n)

空间复杂度: O(1)

class Solution:
    def longestSubarray(self, nums: List[int]) -> int:
        maxVal = max(nums)  # 找到数组中的最大值
        cnt = 0  # 用于记录最长的连续子数组长度
        n = len(nums)  # 数组长度
        i = 0  # 初始索引
        while i < n:
            if nums[i] != maxVal:
                i += 1  # 如果当前元素不是最大值,继续向前移动
            else:
                L = i  # 记录当前最大值的起始索引
                while i < n and nums[i] == maxVal:
                    i += 1  # 继续移动索引直到找到不是最大值的元素
                cnt = max(cnt, i - L)  # 更新最长连续子数组长度
        return cnt  # 返回最长连续子数组的长度

Explore

按位与操作会对二进制的每一位进行与运算,即只有所有位都是1时结果才为1。因此,包含较大数字的子数组在进行按位与操作时,更有可能保持较高的位为1,从而得到更大的结果。特别是最大值本身,其二进制中的高位1在与操作中不会因为其他更小的数而被置0(因为这些数的相应位不能比最大值更高)。因此,包含数组中的最大值的子数组在进行按位与操作时,更可能得到最大的结果。

是的,该算法能正确返回其中一段的长度。算法通过遍历整个数组来确定所有包含maxVal的连续子数组,并利用一个计数器记录下这些子数组中的最大长度。无论maxVal出现在数组中的哪个位置,只要是最长的连续子数组,它们的长度都会被正确记录和返回。

这种方法相对简单易懂,但确实存在一定的优化空间。例如,可以通过一次遍历同时完成最大值的查找和最长连续子数组的确定。在这一次遍历中,同时更新最大值和检查当前值是否为最大值,如果是,则继续计算连续子数组的长度;如果不是,则重置长度计数。这样可以减少一次完整的数组遍历,提高算法效率。

算法在遍历时,会检查每个元素是否等于maxVal,并在找到maxVal时进入内部循环继续向后查找,直到遇到非maxVal元素或达到数组末尾。内部循环的条件包括i < n,保证i不会超出数组的界限,因此不会出现索引越界。当数组末尾是maxVal时,内部循环会正常结束并更新最长长度,然后外部循环由于i等于n也将结束,整个过程安全且正确。