📜  查找 L 到 R 中的数字,这与提高到 setbit 计数的数字总和相同(1)

📅  最后修改于: 2023-12-03 15:26:35.694000             🧑  作者: Mango

查找 L 到 R 中的数字

本文介绍对于给定区间 [L, R],如何查找其中所有的数字。这个问题可以转化为计算位数为 N 的二进制数在 [L, R] 区间内的出现次数,进而使用 setbit 计数算法计算数字总和。

位数为 N 的二进制数在 [L, R] 区间内出现次数

可以使用两个二进制数 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$,由此可以考虑分类讨论:

  1. 当 $2^{N-1} \leq L$ 时,所有第 N 位为 1 的数都在 [L, R] 区间内。因此,对于这些数,它们的第 N+1 至 W 位可以随意取值,因此出现次数为 $2^{W-N-1}$;
  2. 当 $L < 2^{N-1}$ 且 $R < 2^N$ 时,区间 [L, R] 内没有数字的第 N 位为 1,因此出现次数为 0;
  3. 当 $L < 2^{N-1}$ 且 $2^N \leq R$ 时,所有第 N 位为 1 的数都在 [L, $2^N - 1$] 区间内,因此出现次数为 $2^{N-1}$;
  4. 当 $2^{N} \leq L < 2^{N+1}$ 且 $2^N \leq R$ 时,所有第 N 位为 1 的数都在区间 $\max(L, 2^N)$ 到 R 区间内,因此出现次数为 $2^{W-N-1}$。

综上所述,对于位数为 N 的二进制数,我们可以按照以上方法计算它在 [L, R] 区间内的出现次数。

setbit 计数算法

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)$。

我们可以在 LeetCode 题库中找到与之相关的问题:LC 0982LC 0907