题解:洛谷 B4499 二进制回文串
【题目来源】
洛谷:B4499 [GESP202603 三级] 二进制回文串 - 洛谷
【题目描述】
对于一个正整数 \(n\),我们将其转换为不含前导零的二进制表示,如果这个二进制序列从左向右读与从右向左读完全相同,则称该数为二进制回文数。例如,\(9\) 的二进制表示为 \((1001)_2\),是二进制回文数;\(12\) 的二进制表示为 \((1100)_2\),不是二进制回文数。
你的任务是:给定一个正整数 \(n\),计算在 \(1\) 到 \(n\) 的范围内二进制回文数的数量。
【输入】
输入一行,包含一个正整数 \(n\)。
【输出】
输出一行,包含一个数,表示在 \(1\) 到 \(n\) 的范围内二进制回文数的数量。
【输入样例】
15
【输出样例】
6
【核心思想】
-
问题分析:给定正整数 \(n\),需要统计 \([1, n]\) 范围内二进制回文数的个数。二进制回文数是指其二进制表示(不含前导零)从左到右读与从右到左读完全相同的数。例如 \(9 = (1001)_2\) 是回文数,\(12 = (1100)_2\) 不是。本质是进制转换 + 回文判定的组合问题。
-
算法选择:
- 进制转换:将十进制数转换为二进制字符串表示
- 回文判定:将字符串与其反转后的字符串比较,相同则为回文
-
关键步骤:
- 进制转换函数
Dtom(n, m):- 初始化空字符串 \(ans\)
- 当 \(n > 0\) 时循环:
- \(ans \leftarrow d[n \bmod m] + ans\)(将新位插入字符串前端)
- \(n \leftarrow \lfloor n / m \rfloor\)
- 返回 \(ans\)
- 主流程:
- 读入 \(n\)
- 遍历 \(i\) 从 \(1\) 到 \(n\):
- \(t \leftarrow\)
Dtom(i, 2)(转换为二进制字符串) - \(s \leftarrow t\)(保存原串)
reverse(t.begin(), t.end())(反转字符串)- 若 \(t = s\),则
cnt++
- \(t \leftarrow\)
- 输出 \(cnt\)
- 进制转换函数
-
时间/空间复杂度:
- 时间复杂度:\(O(n \cdot \log n)\),每个数转换二进制 \(O(\log i)\),反转和比较 \(O(\log i)\)
- 空间复杂度:\(O(\log n)\),存储单个数的二进制字符串
-
进制转换与回文判定的核心思想:
- 短除法进制转换:通过反复取模和整除,从低位到高位提取 \(m\) 进制下的各位数字,利用字符串前插(
d[n % m] + ans)保证高位在前,得到标准进制表示 - 回文对称性:回文串的正读反读相同,因此将字符串反转后与原串比较即可判定,无需逐位双指针扫描
- 前导零自然消除:短除法转换过程中,当 \(n\) 变为 \(0\) 时停止,最高位不会为 \(0\),天然满足"不含前导零"的要求
- 通用进制框架:
Dtom函数通过字符集d支持最多 \(16\) 进制,本题传入 \(m = 2\) 即得二进制,体现了算法的可复用性 - 适用于进制转换、回文数统计、数字模式识别等基础数论与字符串结合问题
- 短除法进制转换:通过反复取模和整除,从低位到高位提取 \(m\) 进制下的各位数字,利用字符串前插(
【算法标签】
入门 #语法基础
【代码详解】
#include <bits/stdc++.h> // 包含所有标准库头文件
using namespace std; // 使用标准命名空间
int n, cnt; // 定义全局变量:n(输入的数),cnt(计数器,记录符合条件的数的个数)
// 将十进制数n转换为m进制表示的字符串
string Dtom(int n, int m)
{
// 定义字符集,支持最多16进制(0-9, A-F)
string d = "0123456789ABCDEF";
string ans = ""; // 初始化结果字符串为空
// 将十进制数n转换为m进制
while (n > 0)
{
// 获取n除以m的余数,即当前位的值
// 余数作为索引在d中查找对应的字符
// 注意:这里将新得到的位添加到字符串的前面
ans = d[n % m] + ans;
// n除以m,为处理下一位做准备
n /= m;
}
return ans; // 返回转换后的m进制字符串
}
int main() // 主函数入口
{
cin >> n; // 输入整数n
// 遍历1到n的所有整数
for (int i = 1; i <= n; i++)
{
// 将整数i转换为二进制字符串
string t = Dtom(i, 2);
// 保存原始字符串
string s = t;
// 将字符串t反转
reverse(t.begin(), t.end());
// 判断反转后的字符串是否与原字符串相同
// 即判断二进制表示是否是回文数
if (t == s)
{
cnt++; // 如果是回文数,计数器加1
}
}
cout << cnt << endl; // 输出满足条件的数的个数
return 0; // 程序正常结束
}
【运行结果】
15
6
浙公网安备 33010602011771号