目标
给你两个整数 n 和 x 。你需要构造一个长度为 n 的 正整数 数组 nums ,对于所有 0 <= i < n - 1 ,满足 nums[i + 1] 大于 nums[i] ,并且数组 nums 中所有元素的按位 AND 运算结果为 x 。
返回 nums[n - 1] 可能的 最小 值。
示例 1:
输入:n = 3, x = 4
输出:6
解释:
数组 nums 可以是 [4,5,6] ,最后一个元素为 6 。
示例 2:
输入:n = 2, x = 7
输出:15
解释:
数组 nums 可以是 [7,15] ,最后一个元素为 15 。
说明:
- 1 <= n, x <= 10^8
思路
构造一个长度为 n
的单调递增数组 nums
,要求数组所有元素按位与的结果为正整数 x
,返回 nums[n - 1]
的最小值。
要使元素按位与的结果为 x
,那么 x
中bit位为1的位置在元素中也必须为1。我们可以找到 x
中bit位为0的位置,从最低位开始置1,如果还不凑不够数组个数就将最高位置1,重新从最低位开始置1。
刚开始是按照上面的思路模拟着写的,非常容易出错。调试了半天发现,其实就是将 n - 1
的二进制表示填入 x
的二进制表示中的bit位为0的位置,如果不够就借用高位的0。之所以是 n -1
是因为 x
本身也算一个元素,0 ~ n - 1
共 n
个元素。
官网题解也正是这个思路,不过实现简洁多了。主要是刚开始没有想清楚,其实根本不用讨论低位/高位,直接填就行了。
代码
/**
* @date 2024-08-22 10:12
*/
public class MinEnd3133 {
public long minEnd(int n, int x) {
List<Integer> zeroIndex = new ArrayList<>(63);
int i = 0;
int tmp = x;
// 记录0的位置
while (tmp > 0) {
if ((tmp & 1) == 0) {
zeroIndex.add(i);
}
tmp >>= 1;
i++;
}
long res = x;
int zeroCnt = zeroIndex.size();
long highBitsCnt;
// 如果不含0,那么只能填高位
if (zeroCnt == 0) {
// 这个不应该全填1,应填n-1的二进制表示
highBitsCnt = n - 1;
while (highBitsCnt > 0) {
if ((highBitsCnt & 1) == 1) {
res |= 1L << i;
}
i++;
highBitsCnt >>= 1;
}
return res;
}
// 如果含0则计算其能表示的数字个数,下面是快速幂
long oneLoopElementCnt = 1;
tmp = zeroCnt;
int base = 2;
while (tmp > 0) {
if ((tmp & 1) == 1) {
oneLoopElementCnt = oneLoopElementCnt * base;
}
base *= base;
tmp >>= 1;
}
// 低位需要填的个数
long lowBitsCnt = n % oneLoopElementCnt - 1;
if (lowBitsCnt == -1) {
// 如果为-1,表示能够整除,将低位的0全置为1
for (Integer index : zeroIndex) {
res |= 1L << index;
}
// 如果低位能够填满数组则无需填高位
if (n <= oneLoopElementCnt) {
highBitsCnt = 0;
} else {
// 高位的计数需减1,是因为将低位全填1,这时已经完成了一个loop,例如n=4,x=2,oneLoopElementCnt 为2,n / oneLoopElementCnt = 2, (10 11) 110 111,我们只需要填充一个高位
highBitsCnt = n / oneLoopElementCnt - 1;
}
} else {
// 否则将需要填在低位的个数填入低位的0
int j = 0;
while (lowBitsCnt > 0) {
if ((lowBitsCnt & 1) == 1) {
res |= 1L << zeroIndex.get(j);
}
j++;
lowBitsCnt >>= 1;
}
// 如果低位能够填满数组则无需填高位
if (n <= oneLoopElementCnt) {
highBitsCnt = 0;
} else {
// 这里不需要减1,是因为n不能整除oneLoopElementCnt,多余位已经被舍去了,例如n=3,x=2,n / oneLoopElementCnt = 1
highBitsCnt = n / oneLoopElementCnt;
}
}
// 填充高位个数
while (highBitsCnt > 0) {
if ((highBitsCnt & 1) == 1) {
// 出错点:如果写成1,会溢出,必须加上L
res |= 1L << i;
}
i++;
highBitsCnt >>= 1;
}
return res;
}
}