程序员老鬼

汇丰面试通过,给了 offer。。

刚刷到个贴子,说网友在外包干得挺舒服,不加班,节奏稳,结果汇丰突然发来 offer,要他一周内入职,整个人开始纠结。

Image

我看完只想说:这还纠结啥,肯定去汇丰啊!外包的天花板太低了,你今天觉得舒服,是因为还没被淘汰。

汇丰再怎么催,也代表人家认可你,能给你更高的平台、更广的眼界。机会来了还犹豫,等它走了才后悔,那才叫不负责任。

“不想辜负现在的项目”,这想法挺善良,但职场从来不是情感账。你走了项目照样转,你留下待遇不会涨。职场最该忠诚的是自己,而不是某个暂时的岗位。

说到底,外包是温水,汇丰是大海。舒适是暂时的,成长才是长期的。能跳,就别怕浪。【备注:文末可领最新资料】

算法题:寻找最近的回文数

昨晚十一点多在公司楼下吹风,手机叮的一下,小李问我:“哥,最近的回文数怎么写?我写的老有边界问题。”我当场盘了一遍思路,顺手把代码也敲了,你们就跟着这个来,别走弯路哈。

目标:给一个非负整数 n(通常当成字符串处理),找与它“数值距离”最近的不同回文数;如果距离一样,取更小的那个。 难点都在边界:像 9、10、1000、999… 这种拐点,以及中间位进位/借位导致的连锁反应。

怎么稳?我一般构造一小撮“候选回文”,最后挑最接近的:

  1. 镜像原数:把左半边镜到右边,得到 same。注意它可能等于原数,需要剔除。
  2. 前缀±1 再镜像:把“前缀”(含中位)加一、减一,再各镜一次,解决 129→131、199→202、1000→099? 这类进位借位。
  3. 两大保底:10^k + 1(形如 100…001)和 10^k - 1(形如 99…9),专治 999、1000 这种边界。

候选不多,最多五六个,逐个比差值,距离小者胜;平手取更小的值。为防溢出,用 long 比较,输入当字符串。

关键细节

  • 前缀定义:长度 len,取前 (len+1)/2 位(奇数含中位,偶数不含中位)。
  • 镜像方法:把前缀拷到右侧反向位置,奇偶统一处理。
  • 去重:候选可能重复,用 Set。
  • 排除自身:如果候选等于原数,跳过。
  • 数字太大/太小:前缀加一可能长度变长(如 999→1000),减一可能出现前导零,这都交给“两大保底”兜底。

Java 实现(直接可用)

import java.util.*;

publicclassNearestPalindrome{
public String nearestPalindromic(String n){
int len = n.length();
long original = Long.parseLong(n);
        Set<Long> cand = new HashSet<>();

// 两个保底
        cand.add((long)Math.pow(10, len) + 1);        // 1000..0001
        cand.add((long)Math.pow(10, len - 1) - 1);    //  99..99 (len-1位全9)

// 基于前缀的三个:prefix-1, prefix, prefix+1
long prefix = Long.parseLong(n.substring(0, (len + 1) / 2));
for (long p : newlong[]{prefix - 1, prefix, prefix + 1}) {
if (p < 0) continue;
            String pal = buildPal(p, len);
try {
                cand.add(Long.parseLong(pal));
            } catch (NumberFormatException ignore) {} // 极端大数忽略
        }

// 选最近(排除自身)
long ans = -1;
for (long x : cand) {
if (x == original) continue;
if (ans == -1) ans = x;
long diffA = Math.abs(x - original), diffB = Math.abs(ans - original);
if (diffA < diffB || (diffA == diffB && x < ans)) ans = x;
        }
return String.valueOf(ans);
    }

// 用前缀构造长度为 len 的回文
private String buildPal(long prefix, int len){
        String s = String.valueOf(prefix);
        StringBuilder left = new StringBuilder(s);
// 若前缀位数因进位变长,需要只取前半所需长度
if (left.length() > (len + 1) / 2) {
            left.setLength((len + 1) / 2);
            s = left.toString();
        } elseif (left.length() < (len + 1) / 2) {
// 借位导致位数变短,前面补零
while (left.length() < (len + 1) / 2) left.insert(0, '0');
            s = left.toString();
        }

        StringBuilder sb = new StringBuilder();
        sb.append(s);
// 偶数:镜全部;奇数:跳过中位再镜
int start = (len % 2 == 0) ? s.length() - 1 : s.length() - 2;
for (int i = start; i >= 0; i--) sb.append(s.charAt(i));
return sb.toString();
    }

// 小测
publicstaticvoidmain(String[] args){
        NearestPalindrome np = new NearestPalindrome();
        System.out.println(np.nearestPalindromic("1"));       // 0
        System.out.println(np.nearestPalindromic("9"));       // 8
        System.out.println(np.nearestPalindromic("10"));      // 9
        System.out.println(np.nearestPalindromic("1283"));    // 1331
        System.out.println(np.nearestPalindromic("999"));     // 1001
        System.out.println(np.nearestPalindromic("1000"));    // 999
        System.out.println(np.nearestPalindromic("12321"));   // 12221
    }
}

为啥这套稳定

因为“候选法”把所有会出事的场景都覆盖了:

  • 镜像原数保证“就地修复”;
  • 前缀±1解决中位进位/借位连锁;
  • 10^k±1 把位数变化的边界牢牢兜住。 最后统一用差值+字典序规则挑选,就很稳。行了我先去泡杯茶,哪个测试还卡住,丢给我看下具体样例就行。

-END-

我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html

最后给大家分享一份不错的副业资料,点击下方公众号,回复关键字: 副业 领取,也可以链接我领取,微信:hls404