周賽 395。

題目

輸入兩個整數 n 和 x。
你必須構造一個大小 n 的整數陣列 nums,對於每個 0 <= i < n - 1,滿足 nums[i + 1] 大於 nums[i]。
且 nums 中所有元素做 AND 運算後的結果為 x。

求 nums[n - 1] 可能的最小值

解法

首先複習 AND 運算的特性:只少不多。做越多次運算,越可能使結果值變小。
題目要求 AND 的結果要是 x,所以 nums 中每個元素都必須擁有和 x 同樣的位元

同時,題目又要求 nums 呈嚴格遞增,又要求 nums 最後一個數越小越好。
為使得最後一個數盡可能小,則每次增量要盡可能小

但考慮到 AND 的限制,有時候進位反而會把要保留的 1 位元弄不見,所以每次增量可能不同。
例如:

n = 3, x = 2
nums = [2, 3, 6]


先不管 AND 對於 1 位元的限制,考慮 x = 0 應該如何增量:

x = 0 = 0b000
增量 1 得到 0b001
增量 1 得到 0b010
增量 1 得到 0b011
增量 1 得到 0b100

這時回到剛才的例子,被固定的 1 位元就以井號 # 代替,考慮如何增量:

x = 2 = 0b010 = 0b00#0
增量 得到 0b00#1
增量 得到 0b01#0
增量 得到 0b01#1
增量 得到 0b10#0

發現只要忽略 # 號,實際上兩個例子變化順序是相同的。


我們要求 x 開始的第 n 個數字,就需要從 x 開始增量 need = n - 1 次。
以 need 的二進制表示,便是增量 n - 1 次的最終結果。
將 need 二進制的各位元從右到左填入 x 二進制中為 0 的位置即可。

例如:

n = 20, x = 10
x = 0b0000110 先忽略 x 中不可以修改的位置,x = 0b0000##0
need = n - 1 = 19 = 0b10011
從右到左填入 need 的二進位位元
0b1001##1
答案 0b1001111 = 79

時間複雜度 O(log n + log x)。
空間複雜度 O(1)。

class Solution:
    def minEnd(self, n: int, x: int) -> int:
        need = n - 1
        i = 0
        ans = x
        while need > 0:
            # find next 0 bit from x
            while ans & (1 << i):
                i += 1
            # fill bit from bin(need)
            bit = need & 1
            if bit == 1:
                ans |= (1 << i)
            # next bit
            i += 1
            need >>= 1
            
        return ans