中心对称数判定:从回文数到字符映射与双指针实现

发布时间:2026/11/2 22:35:39
中心对称数判定:从回文数到字符映射与双指针实现 1. 从一道月赛题看“中心对称数”的判定思路第一次看到“中心对称数”这个题目名很多人脑子里冒出来的第一反应是“回文数”觉得把数字反过来读一遍比对一下不就完了。真动手写的时候才发现回文和中心对称压根不是一回事。回文是“正着读反着读一样”比如 121、1331而中心对称数说的是把整个数字旋转 180 度之后看起来还是原来那个数。这两个概念在纸面上很像落到代码里判定逻辑完全不同这也是这道题当年让不少丙组选手栽跟头的地方。我先把结论摆出来中心对称数的本质是一次“字符映射 顺序翻转”的联合变换映射规则只有固定的几组翻转则是把整个字符串倒序。任何一位上出现了不在映射表里的数字直接判否映射完之后再和原串比对一致才算数。听起来简单但真正写起来边界条件、前导零、单位数、非法字符这几处全是坑。这篇内容适合谁看如果你正在带学生打基础算法赛、自己刷字符串处理题、或者想给孩子讲清楚“回文”和“旋转对称”的区别那这篇基本能覆盖你需要的全部细节。我会从题目背后的判定模型讲起把映射表怎么来的、代码怎么组织、哪些地方容易写错一层层拆开。哪怕你之前完全没接触过这类题跟着走一遍也能自己写出一个稳的版本。需要先说明一点下面涉及的代码结构、测试用例、排查方法都是基于这类字符串映射题在常见教学和训练场景下的通用做法做的合理补全不是对某份原始题解的照搬。重点在于把“为什么这么设计”讲透让你能迁移到同类型的其他题目上。2. 核心判定模型拆解为什么不能当成回文来做2.1 中心对称数的数学定义与直观理解先把概念钉死。一个数如果把它整体旋转 180 度后得到的图形和原来完全重合它就是中心对称数。注意这里说的是“数字的图形”不是数值运算。0 转 180 度还是 01 还是 18 还是 8这三个是“自对称”的6 转过来变成 99 转过来变成 6这两个是“互换对称”的2、3、4、5、7 这五个数字转 180 度之后根本不成形属于非法字符。所以判定一个数是不是中心对称数等价于做两件事逐位检查每个数字是否在合法集合 {0, 1, 6, 8, 9} 里把每一位按映射规则替换0→01→16→98→89→6同时把整个序列倒序看结果是否和原序列完全一致。这里有个特别容易被忽略的点倒序和映射是同时发生的不是先映射再倒序或者先倒序再映射的简单叠加。从结果上看两者等价但从理解上必须清楚旋转 180 度这个动作本身就包含了“位置翻转”和“字符变换”两个维度。很多同学写代码时只做了映射忘了倒序或者只倒序忘了映射测试用例一跑就露馅。举个具体例子。数字 69逐位检查 6 和 9 都合法映射后 6→9、9→6得到 96再倒序得到 69和原数一致所以 69 是中心对称数。再看 96映射后 9→6、6→9 得 69倒序得 96也一致所以 96 也是。但 66 呢映射后 6→9、6→9 得 99倒序还是 99和原数 66 不等所以 66 不是中心对称数。这个例子最能说明问题——66 是回文但不是中心对称数两个概念在这里彻底分道扬镳。2.2 与回文数判定的本质差异把差异列成表会更清楚。回文判定只关心“正反读是否一致”中心对称判定关心的是“旋转后是否一致”旋转这个动作引入了字符映射这是回文完全没有的环节。对比维度回文数中心对称数核心操作字符串反转后比对字符映射 反转后比对合法字符集0-9 全部仅 {0,1,6,8,9}典型正例121、1331、9969、88、609、1001典型反例123、45666、121、123前导零敏感度一般不敏感高度敏感这张表里最值得盯的是最后一行。回文题里前导零通常不是大问题因为数值本身不会带前导零但中心对称数如果按字符串处理前导零会直接影响判定结果。比如 “010”映射后 0→0、1→1、0→0 得 010倒序还是 010按字符串规则它是中心对称的但如果当成整数 10 来处理情况就变了。所以输入到底按字符串读还是按整数读必须在动手前想清楚这是这类题第一个分水岭。2.3 判定流程的伪代码级梳理把上面的逻辑整理成流程大概是这个顺序读入待判定的数统一按字符串保存避免数值运算丢失前导零或溢出从左到右扫描每一位遇到不在 {0,1,6,8,9} 里的字符立即返回“否”对每一位做映射替换得到一个新串把新串整体反转比较反转后的新串与原串是否相等相等为“是”否则为“否”。这个流程里第 2 步和第 3 步可以合并成一次遍历边检查边映射效率更高。第 4 步和第 5 步也可以合并用双指针从两端向中间夹一边映射左端一边映射右端同时比对。合并之后代码更短但可读性会下降初学者建议先按五步走通再考虑优化。提示判定流程里“先检查合法性再映射”这个顺序不能反。如果先映射再检查遇到非法字符时映射表里查不到对应项程序行为就不可控了轻则输出错误重则数组越界。3. 映射表设计与代码实现细节3.1 映射表的三种组织方式与选型理由映射关系只有五组怎么存都行但不同存法在代码清晰度和运行效率上有差别。常见的有三种数组直存法开一个长度 10 的数组下标是原数字值是映射后的数字非法位置填 -1 做标记。查表 O(1)代码最直观。哈希表法用 map 或 unordered_map 存五组键值对查不到就说明非法。灵活但有小常数开销对这道题的数据规模来说属于杀鸡用牛刀。条件分支法写一串 if-else 或者 switch把五种情况列出来。代码最短但扩展性差加一组映射就要改结构。我个人的选择是数组直存法理由很实在数字只有 0 到 9 十个数组天然贴合下标语义查表就是一次内存访问没有任何额外开销非法位置用 -1 标记判断逻辑统一成“查表结果是否为 -1”不用写一堆分支。哈希表在这道题里属于过度设计条件分支则在需要维护时容易漏改。数组的初始化大概长这样int mp[10] {0, 1, -1, -1, -1, -1, 9, -1, 8, 6};逐个解释下标 0 对应 0下标 1 对应 1下标 2 到 5 全是 -1非法下标 6 对应 9下标 7 是 -1下标 8 对应 8下标 9 对应 6。写完之后拿几个数验一下mp[6] 应该是 9mp[9] 应该是 6mp[2] 应该是 -1对上了就说明表没写错。注意这张表最容易写错的是 6 和 9 的位置。有人习惯按“6 变 9、9 变 6”的顺序写结果把 mp[6] 写成 6、mp[9] 写成 9那就变成自对称了整个判定全错。写完一定拿 69 这个数跑一遍验证。3.2 双指针夹逼实现与单次遍历实现对比实现层面有两条路。第一条是双指针夹逼左指针从头、右指针从尾同时向中间走每次检查左指针字符的映射是否等于右指针字符同时检查右指针字符的映射是否等于左指针字符。两个条件都满足才继续任一不满足直接判否。bool isCentrosymmetric(const string s) { int mp[10] {0, 1, -1, -1, -1, -1, 9, -1, 8, 6}; int i 0, j (int)s.size() - 1; while (i j) { int a s[i] - 0; int b s[j] - 0; if (a 0 || a 9 || b 0 || b 9) return false; if (mp[a] -1 || mp[b] -1) return false; if (mp[a] ! b || mp[b] ! a) return false; i; j--; } return true; }这段代码的关键在mp[a] ! b || mp[b] ! a这一行。为什么两个方向都要比因为旋转是对称操作左边的字符映射后应该等于右边的字符右边的字符映射后也应该等于左边的字符两个条件缺一不可。只比一个方向的话像 68 这种“左映射等于右但右映射不等于左”的情况就会漏判——68 里 6 映射成 9 不等于 8其实第一个条件就挂了但换成别的组合就可能出问题所以两个方向都写上是稳妥做法。第二条路是单次遍历构造新串再比对代码更贴近前面讲的五步流程可读性好但要多开一个字符串空间。数据规模小的时候两者没差别规模大的时候双指针省空间。我一般教学时先讲单次遍历版让学生把逻辑走通再讲双指针版做优化这样理解曲线更平缓。3.3 边界条件清单单位数、偶数位、含零串边界条件是这类题的重灾区我整理了一份清单写代码前对着过一遍能省很多调试时间。边界情形示例判定结果说明单个自对称数字0、1、8是映射后不变天然满足单个互换对称数字6、9是映射后互换但单位数反转后位置不变需特殊确认含非法数字2、3、4、5、7否直接判否偶数位全合法69、88、609视情况需完整走映射加反转奇数位中间位60809视情况中间位必须是自对称数字含前导零010视情况按字符串处理时需明确规则这里单独说奇数位的情况。长度为奇数的串正中间那一位在旋转后位置不变所以它必须映射到自己也就是只能是 0、1、8 这三个自对称数字。6 和 9 放在中间位是不行的因为旋转后 6 变 9、9 变 6中间位就变了整个数不可能和原来重合。这个细节在双指针实现里会自动被覆盖——当 i 等于 j 时条件退化成mp[a] a正好把中间位的约束表达出来了不用额外写判断。这也是双指针写法优雅的地方。单位数 6 和 9 值得单独拎出来说。按双指针逻辑i 和 j 都指向同一个字符条件变成 mp[6] 是否等于 6答案是否所以 6 会被判成“否”。但直觉上 6 旋转 180 度确实变成 9不是 6所以判否是对的。这里容易产生的困惑是“6 和 9 互为对称那 6 自己算不算中心对称”答案是不算因为它旋转后变成了另一个数。只有 0、1、8 这三个单位数算中心对称数。4. 完整实操流程与测试验证4.1 从读入到输出的完整代码骨架把前面的零件组装起来一个可直接运行的版本大概是这样。我按“读入、判定、输出”三段式组织中间判定部分封装成函数方便单独测试。#include iostream #include string using namespace std; bool isCentrosymmetric(const string s) { int mp[10] {0, 1, -1, -1, -1, -1, 9, -1, 8, 6}; int i 0, j (int)s.size() - 1; while (i j) { int a s[i] - 0; int b s[j] - 0; if (a 0 || a 9 || b 0 || b 9) return false; if (mp[a] -1 || mp[b] -1) return false; if (mp[a] ! b || mp[b] ! a) return false; i; j--; } return true; } int main() { string s; cin s; cout (isCentrosymmetric(s) ? Yes : No) endl; return 0; }这段代码有几个设计取舍值得说明。第一输入用 string 而不是 long long原因是题目里的数可能很长超出整数范围而且前导零在字符串里能原样保留判定语义更清晰。第二判定函数参数用 const 引用避免拷贝开销也表明函数内部不修改原串。第三输出用三元表达式一行搞定不写多余的 if-else保持主干清爽。如果题目要求输出的是“是/否”之外的具体信息比如“第几位非法”或者“映射后的结果”那就在判定函数里加输出或者改成返回结构体主干逻辑不变。这种分层写法在比赛里改需求时特别省事。4.2 手工推演三个典型用例代码写完不能直接交得手工推几个用例确认逻辑。我挑三个最有代表性的走一遍。用例一69。i0 指向 6j1 指向 9。a6b9。mp[6]9mp[9]6都合法。检查 mp[a]b 即 99 成立mp[b]a 即 66 成立。i 增到 1j 减到 0循环结束返回真。结论69 是中心对称数符合预期。用例二66。i0 指向 6j1 指向 6。a6b6。mp[6]9mp[6]9都合法。检查 mp[a]b 即 96 不成立直接返回假。结论66 不是中心对称数。这个用例专门用来抓“把中心对称当回文做”的错误如果代码判 66 为真说明映射环节漏了。用例三60809。长度 5中间位是 8。i0 对 j46 对 9mp[6]9 等于 9mp[9]6 等于 6通过。i1 对 j30 对 0mp[0]0 等于 0通过。i2 对 j28 对 8mp[8]8 等于 8通过。循环结束返回真。结论60809 是中心对称数。这个用例覆盖了奇数位和中间位自对称的情况。三个用例跑完基本逻辑就验证得差不多了。如果还有余力可以再加一个含非法字符的用例比如 123预期在 i0 时 mp[1]1 合法但 mp[3]-1 非法返回假确认非法字符拦截生效。4.3 批量测试与对拍思路单个用例过了不代表全对稳妥做法是造一批数据批量跑。手工造数据可以从几个维度覆盖长度从 1 到 6 各来几个合法字符和非法字符混合前导零单独造一组全自对称数字造一组全互换对称数字造一组。更进一步的做法是写一个暴力对拍程序。暴力版的思路很直接把原串按映射规则逐位替换得到新串再把新串反转最后和原串做字符串比较。这个暴力版逻辑简单不容易写错用它去和双指针版对拍随机生成几千组数据结果不一致就打印出来人工看。对拍是发现边界 bug 最有效的手段尤其是前导零和奇数位这两类情况肉眼很难穷举交给随机数据去撞。提示对拍时随机数据的字符集要包含非法数字否则永远测不出非法字符拦截逻辑的问题。建议按 70% 合法字符加 30% 非法字符的比例生成覆盖面比较均衡。5. 常见错误与排查实录5.1 五类高频错误速查表带过几轮基础训练之后我发现这类题的错误高度集中翻来覆去就那几类。整理成表写完之后对着自查一遍能拦下大部分问题。错误类型典型表现根因修正方法混淆回文与中心对称66 被判为真只做了反转没做映射补上映射表两个方向都比对映射表写反69 被判为假mp[6] 和 mp[9] 写反或写成自对称用 69 单测验证映射方向漏判非法字符含 2、3、4、5、7 的串被判为真查表没判 -1映射前先检查是否为 -1前导零处理不当010 判定结果与预期不符输入按整数读导致丢零统一按字符串读入奇数位中间位漏判668 被判为真中间位没检查自对称双指针 ij 时条件自动覆盖这张表里最隐蔽的是最后一行。668 这个串两端 6 和 8 的映射检查mp[6]9 不等于 8第一个条件就挂了所以其实不会误判。真正会误判的是 606 这种两端 6 和 6 映射后都是 9互相检查 9 对 6 不成立也会挂。中间位漏判的典型是 616 之外的组合比如 686两端 6 和 6 检查时 mp[6]9 不等于 6挂。所以严格说中间位漏判在双指针写法里不太容易出现但在“先构造新串再比对”的写法里如果构造时把中间位映射错了就会出问题。这也是我推荐双指针写法的原因之一边界约束被结构本身兜住了。5.2 调试时怎么快速定位是哪一位出的问题判定函数返回假的时候光知道“假”没用得知道是哪一位挂的。最快的办法是在判定循环里加临时输出把 i、j、a、b、mp[a]、mp[b] 都打出来跑一个失败用例一眼就能看出是映射错还是比对错。比如跑 69 输出假打印出来发现 mp[6] 是 6 而不是 9那就定位到映射表写错了。跑 66 输出真打印出来发现根本没进映射比对分支那就定位到条件判断写漏了。这种“打印中间状态”的土办法在字符串题里比单步调试还快因为字符串短打印出来一目了然。另一个技巧是把判定函数单独抽出来写个小 main 测试不要和输入输出混在一起。比赛时时间紧可能顾不上但平时训练一定要养成这个习惯函数级测试比整体测试定位快得多。5.3 从这道题能迁移出去的通用套路中心对称数这道题的价值不在题目本身而在于它示范了一类“字符映射加序列变换”的题型。同类型的还有镜像字符串、旋转字符串、字母异位词判定等等套路高度一致先定映射表再定变换规则最后做比对。把这道题的映射表设计和双指针比对吃透遇到同类题基本就是换张表的事。我在实际带练时的体会是这类题最该练的不是写代码的速度而是动手前把映射关系和变换顺序在纸上画清楚。画清楚了代码二十分钟能写完画不清楚写两小时还在调边界。中心对称数这道题看着简单但 66 和 69 这两个用例往那一摆就能筛出到底有没有真正理解“旋转”和“反转”的区别。这个区分能力比多做十道回文题有用得多。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询