📅  最后修改于: 2023-12-03 15:26:35.694000             🧑  作者: Mango
本文介绍对于给定区间 [L, R],如何查找其中所有的数字。这个问题可以转化为计算位数为 N 的二进制数在 [L, R] 区间内的出现次数,进而使用 setbit 计数算法计算数字总和。
可以使用两个二进制数 bL 和 bR 分别记录 L 和 R 的二进制表示。其中 bL 和 bR 的位数相等(设为 W),不足的高位补 0,如 L = 3,R = 10,W = 4,则 bL = 0011,bR = 1010。
假设我们要计算位数为 N 的二进制数在 [L, R] 区间内出现的次数。如何计算呢?注意到,若一个二进制数 的第 N 位(自右向左数,从 0 开始)为 1,那么它在 [L, R] 区间内要么已经越界,要么满足 $2^N \leq x \leq R$,由此可以考虑分类讨论:
综上所述,对于位数为 N 的二进制数,我们可以按照以上方法计算它在 [L, R] 区间内的出现次数。
setbit 计数算法针对二进制数的每一位,统计这一位上所有数位的和。具体而言,我们枚举二进制数的每一位,将这一位为 1 的数的数字统计加入答案。那么,如何计算第 i 位上所有数的数字和呢?可以设当前位为 $2^i$,然后计算 $2^i$ 在区间 [L,R] 内出现的次数,最后将次数乘以 $2^i$ 得到贡献值。
最终的代码如下:
// 计算位数为 N 的二进制数在 [L, R] 区间内的出现次数
private static long count(long L, long R, int N) {
long bL = Long.parseLong(Long.toBinaryString(L)), bR = Long.parseLong(Long.toBinaryString(R));
int W = Long.toBinaryString(R).length();
if (bL >= (1L << (N - 1))) { // 第 N 位及其后面全是 1
if (1L << N <= R) return (1L << (W - N - 1));
else return (int) (R - L + 1);
} else if (bR < (1L << N)) { // 第 N 位及其前面全是 0
return 0;
} else if (L < (1L << (N - 1))) { // 第 N 位是 0,第 N-1 位是 1
return (1L << (N - 1));
} else { // 第 N 位是 1,第 N-1 位是 1
return (1L << (W - N));
}
}
// 计算 [L, R] 区间内所有数字 setbit 计数的总和
private static long sumOfSetbit(long L, long R) {
long res = 0;
for (int i = 0; i <= 63; i++) {
long cnt = count(L, R, i);
if (cnt > 0) res += cnt * (1L << i);
}
return res;
}
时间复杂度为 $O(\log R)$,空间复杂度为 $O(1)$。