试题要求

对于一个正整数 ,我们将其转换为不含前导零的二进制表示,如果这个二进制序列从左向右读与从右向左读完全相同,则称该数为二进制回文数。例如,9 的二进制表示为(1001)2 ,是二进制回文数; 12的二进制表示为 (1100)2,不是二进制回文数。 你的任务是:给定一个正整数 ,计算在1 到 n 的范围内二进制回文数的数量。

输入格式

输入一行,包含一个正整数 。

输出格式

输出一行,包含一个数,表示在 到 的范围内二进制回文数的数量。

输入样例

15

输出样例

6

样例解释

样例 1 中,1 到 15 范围内 1、3 、5 、7 、9 、15 是二进制回文数。

数据范围:

$1\leq n\leq 10^5$

试题解析

一、题目在说什么?

题目给我们一个正整数 n,要求统计:

1n 之间,有多少个数的二进制表示是“回文”的。

什么叫二进制回文数? 先把一个整数转换成二进制,然后看这个二进制字符串是不是左右对称。 例如:

9 的二进制是 1001

从左往右读是:

1001

从右往左读也是:

1001

所以 9 是二进制回文数。 再看:

12 的二进制是 1100

反过来是:

0011

不一样,所以 12 不是二进制回文数。


二、样例为什么输出 6?

输入:

15

我们检查 115

十进制 二进制 是否回文
1 1
2 10
3 11
4 100
5 101
6 110
7 111
8 1000
9 1001
10 1010
11 1011
12 1100
13 1101
14 1110
15 1111

所以符合条件的是:

1, 3, 5, 7, 9, 15

一共有 6 个,因此输出:

6

三、解题思路

这道题的数据范围是:

1 ≤ n ≤ 100000

n 最大只有十万,不算特别大。 所以我们可以直接从 1 枚举到 n,每个数都做两件事:

  1. 把这个数转换成二进制字符串;
  2. 判断这个二进制字符串是不是回文。

如果是回文,答案就加 1


四、如何把十进制转成二进制?

比如我们要把 9 转成二进制。 可以不断除以 2,记录余数:

9 ÷ 2 = 4 余 1
4 ÷ 2 = 2 余 0
2 ÷ 2 = 1 余 0
1 ÷ 2 = 0 余 1

余数从下往上读,就是:

1001

在程序中,我们可以这样写:

string toBinary(int x) {
    string s = "";

    while (x > 0) {
        int r = x % 2;
        s += char('0' + r);
        x /= 2;
    }

    reverse(s.begin(), s.end());
    return s;
}

这里要注意: 我们一开始得到的余数顺序是反的,所以最后要 reverse 一下。


五、如何判断一个字符串是不是回文?

比如:

1001

我们可以用两个指针:

左指针 l 指向开头
右指针 r 指向结尾

然后不断比较:

s[l] 是否等于 s[r]

如果有一次不相等,就说明不是回文。 如果一直都相等,就是回文。

代码如下:

bool isPalindrome(string s) {
    int l = 0;
    int r = s.size() - 1;

    while (l < r) {
        if (s[l] != s[r]) {
            return false;
        }
        l++;
        r--;
    }

    return true;
}

六、完整代码

#include <bits/stdc++.h>
using namespace std;

string toBinary(int x) {
    string s = "";

    while (x > 0) {
        int r = x % 2;
        s += char('0' + r);
        x /= 2;
    }

    reverse(s.begin(), s.end());
    return s;
}

bool isPalindrome(string s) {
    int l = 0;
    int r = s.size() - 1;

    while (l < r) {
        if (s[l] != s[r]) {
            return false;
        }
        l++;
        r--;
    }

    return true;
}

int main() {
    int n;
    cin >> n;

    int ans = 0;

    for (int i = 1; i <= n; i++) {
        string b = toBinary(i);

        if (isPalindrome(b)) {
            ans++;
        }
    }

    cout << ans << endl;

    return 0;
}

七、复杂度分析

n 最大是 100000。 一个数转换成二进制后,长度大约是 log₂n。 因为:

100000 的二进制长度不到 20 位

所以每个数最多处理大约 20 个字符。 总时间复杂度大约是:

O(n log n)

对于 n ≤ 100000 来说,完全可以通过。


八、这道题的核心

这道题并不难,关键是理解三个概念:

  1. 二进制表示:把十进制数转换成只含 01 的形式;
  2. 回文:正着读和反着读一样;
  3. 枚举:从 1n 一个一个检查。

所以整体思路就是:

枚举每个数 → 转成二进制 → 判断是否回文 → 统计数量

这是一道非常适合练习“进制转换”和“字符串判断”的题目。

文章分享二维码

微信扫码分享这篇文章

扫描二维码即可在手机中继续阅读,也方便转发给老师、家长或同学。

上一篇 已经是最新文章
下一篇 中国邮递员问题:一个中国数学家提出的算法,正在改变世界