LeetCode 564.寻找最近的回文数

Posted Tisfy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 564.寻找最近的回文数相关的知识,希望对你有一定的参考价值。

564.寻找最近的回文数

力扣题目链接

给定一个表示整数的字符串 n n n ,返回与它最近的回文整数(不包括自身)。如果不止一个,返回较小的那个。

“最近的”定义为两个整数差的绝对值最小。

示例 1:

输入: n = "123"
输出: "121"

示例 2:

输入: n = "1"
输出: "0"
解释: 0 和 2是最近的回文,但我们返回最小的,也就是 0。

提示:

  1. 1 ≤ n . l e n g t h ≤ 18 1 \\leq n.length \\leq 18 1n.length18
  2. n n n 只由数字组成
  3. n n n 不含前导 0 0 0
  4. n n n 代表在 [ 1 , 1 0 18 − 1 ] [1, 10^18 - 1] [1,10181] 范围内的整数

思路

本题主要思路就是贪心。要想使修改后和修改前差别尽可能小,最容易想到的就是优先修改低位的数字。

但是回文串中,低位的修改可能会导致高位同步修改,因此,最小代价的改动就是修改中间的值。

比如数字 222 222 222 本身就是回文串,要变成与它绝对值之差最小的回文串,如果把最低位的 2 2 2 修改为 1 1 1,那么最高位的 2 2 2 也要相应地修改为 1 1 1,因此 222 222 222 就变成了 121 121 121 d i f f = a b s ( 121 − 222 ) = 101 diff=abs(121-222)=101 diff=abs(121222)=101 。但是如果我们优先修改中间的值,把十位的 2 2 2 修改为 1 1 1 222 222 222 就会变成 212 212 212 d i f f = a b s ( 212 − 222 ) = 10 diff=abs(212-222)=10 diff=abs(212222)=10

总之,尽可能优先地修改中间的值即可。修改完中间的值后,只需要把后半部分变成前半部分的对称即可。因为是尽可能小地改动,所以我们只需要考虑 前 半 部 分 − 1 前半部分-1 1 前 半 部 分 不 变 前半部分不变 前 半 部 分 + 1 前半部分+1 +1 3 3 3 种情况,并把后半部分变成前半部分的对称即可。

特殊情况:

  1. 当给定数字本身就是个位数时,直接 原 数 字 − 1 原数字-1 1 即可。

  2. 存在一些特殊的数字,采用上述贪心策略无法得到最优解。

    例如 999 999 999 ,按照上述贪心策略可能会考虑 前半部分 99 + 1 = 100 99+1=100 99+1=100 的情况,这样前后做对称就变成了 100001 100001 100001 ,显然 999 999 999 变成 1001 1001 1001 才是更优解。同理, 10023 10023 10023 按上述策略会变成 999 999 999 ,但其实 9999 9999 9999 才是最优解。

    这两种情况是由“前半部分±1后数字位数发生变化”导致的,我们在考虑最优解的时候,把形如 100..001 100..001 100..001 99..99 99..99 99..99 的数字也考虑进去即可。

AC代码

C++

typedef long long ll;

class Solution 
public:
    string nearestPalindromic(string n) 
        // 如果直接就是个位数,就返回n-1即可
        if (n.size() == 1) 
            n[0] -= 1;
            return n;
        
        // 候选的答案(包括 99..99、100..001、前半部分+1做对称、前半部分不变做对称、前半部分-1做对称)
        vector<ll> condidates = 
            (ll)pow(10, n.size()) + 1,  // 100..001  例如99有2位,因此有候选答案101(10 ^ 2 + 1)
            (ll)pow(10, n.size() - 1) - 1  // 99..99  例如100有3位,因此就有候选答案99(10 ^ (3 - 1) - 1)
        ;
        string half = n.substr(0, (n.size() + 1) / 2);  // 前半部分  如果是三位数就取2位,四位数也取1位,两位数就取1位,因此前半部分的长度是⌊(n.size() + 1) / 2⌋
        auto repair = [&](string qian)  // 由前半部分还原为整个数字的函数
            /* 
                整个数字:前半部分qian + 后半部分
                    后半部分:
                        如果原数是奇数位,前半部分的最低位就不需要再重复一次(123的前半部分是12,还原成对称的整个数字是121,2不需要重复)
                        如果原数是偶数位,前半部分的最低为还需要重复一次(1234的前半部分是12,还原成1221,2需要重复)
                        因此原数字是奇数位的话前半部分的迭代器rbegin()+1正好跳过最低位,偶数rbegin()+0就正好包含了最低位
            */
            return qian + string(qian.rbegin() + (n.size() & 1), qian.rend());
        ;
        for (int i = -1; i <= 1; i++)   // 前半部分 +1、不变、-1
            /*
                stol(half):前半部分(字符串)变成long long类型的数字
                stol(half) + i:前半部分 +1、不变 或 -1
                to_string(stol(half) + i): repair函数接收的数据类型是string,因此把long long类型的数字变成string类型
                repair(to_string(stol(half) + i)):由前半部分还原出整个数字
                stol(repair(to_string(stol(half) + i))):把repair函数返回的string类型的数字转换为long long类型
                condidates.push_back(stol(repair(to_string(stol(half) + i)))):将新的候选数字加入候选数字集合中
            */
            condidates.push_back(stol(repair(to_string(stol(half) + i))));
        
        ll m = 1e18, ans = -1;  // m:新回文数 与 原数字 最小的diff  ans:答案
        for (ll& thisAns : condidates)   // 对于所有的候选数字
            if (thisAns != stol(n) && m >= abs(thisAns - stol(n)))   // 首先不是原数,其次这个diff不大于之前候选数字的最小diff
                if (m == abs(thisAns - stol(n)))   // 如果这个diff正好等于之前的最小diff
                    ans = min(ans, thisAns); // 取最小的那个候选数字 (121 与 111、131 的 diff 都是10,但据题意我们需要更小的111)
                
                else   // 否则更新答案为 能使diff更小的这个候选数字
                    m = abs(thisAns - stol(n));
                    ans = thisAns;
                
            
        
        return to_string(ans);  // 返回string类型的答案
    
;

原创不易,转载请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/123254172

以上是关于LeetCode 564.寻找最近的回文数的主要内容,如果未能解决你的问题,请参考以下文章

leetcode------564. 寻找最近的回文数

564. 寻找最近的回文数

LeetCode 6. Z 字形变换 / 564. 寻找最近的回文数 / 258. 各位相加

LeetCode刷题总结-字符串篇

2022-02-17:寻找最近的回文数。 给定一个表示整数的字符串 n ,返回与它最近的回文整数(不包括自身)。如果不止一个,返回较小的那个。 “最近的”定义为两个整数差的绝对值最小。 示例 1: 输

leetcode 564,546