给定一个整数 n ,你需要找到与它最近的回文数(不包括自身)。
“最近的”定义为两个整数差的绝对值最小。
示例 1:
输入: "123"
输出: "121"
注意:
n 是由字符串表示的正整数,其长度不超过18。
如果有多个结果,返回最小的那个。
思路分析:
如果数组的字符串长度 == 1,数字n - 1
开头为1,9**9为一个候选答案 例:100000,答案为99999
开头为9, 10**01为一个候选答案 例:99999,答案为100001
如果本身对称,则把最中间的一个(或两个)位数减(如果0则加)一
例:123321,答案为122221
例:120021,答案为121121
如果不对称:
把前半部分逆序替换掉后半部分 例:1223,答案为1221
把最中间的一个(或两个)位数加一 例:1283,答案为1331,而非1221
把最中间的一个(或两个)位数减一 例:1800,答案为1771,而非1881
class Solution {
public:
string nearestPalindromic(string n) {
int strSize = n.size();
vector<string> candidate;
if (strSize == 1) {
//如果数组的字符串长度 == 1,数字n - 1
return to_string(stoi(n) - 1);
}
if (n[0] == '1') {
//开头为1,9**9为一个候选答案 例:100001,答案为99999
candidate.push_back(string(strSize - 1, '9'));
}
else if (n[0] == '9') {
//开头为9,10**01为一个候选答案 例:99999,答案为100001
candidate.push_back('1' + string(strSize - 1, '0') + '1');
}
if (isPalindromic(n)) {
//如果本身对称,则把最中间的一个(或两个)位数减(如果0则加)一
string temp = n;
if (strSize % 2) {
if (temp[strSize / 2] == '0') {
temp[strSize / 2] += 1;
}
else {
temp[strSize / 2] -= 1;
}
}
else {
if (temp[strSize / 2] == '0') {
temp[strSize / 2 - 1] += 1;
temp[strSize / 2] += 1;
}
else {
temp[strSize / 2 - 1] -= 1;
temp[strSize / 2] -= 1;
}
}
candidate.push_back(temp);
}
else {
//如果不对称:
//把前半部分逆序替换掉后半部分 例:1223,答案为1221
//把最中间的一个(或两个)位数加一 例:1283,答案为1331,而非1221
//把最中间的一个(或两个)位数减一 例:1800,答案为1771,而非1881
//先把钱半部分逆序替换到后半部分
string temp = n.substr(0, strSize / 2), revTemp = temp;
reverse(revTemp.begin(), revTemp.end());
if (strSize % 2) {
temp += n[strSize / 2];
}
temp += revTemp;
candidate.push_back(temp);//"955166082267348992"转换成"955166082280661559"
//把最中间的一个(或两个)位数加一、减一
if (strSize % 2) {
if (temp[strSize / 2] != '0') {
string strT = temp;
strT[strSize / 2] -= 1;
candidate.push_back(strT);
}
if (temp[strSize / 2] != '9') {
string strT = temp;
strT[strSize / 2] += 1;
candidate.push_back(strT);
}
}
else {
if (temp[strSize / 2] != '0') {
string strT = temp;
strT[strSize / 2 - 1] -= 1;
strT[strSize / 2] -= 1;
candidate.push_back(strT);
}
if (temp[strSize / 2] != '9') {
string strT = temp;
strT[strSize / 2 - 1] += 1;
strT[strSize / 2] += 1;
candidate.push_back(strT);
}
}
}
//按照数值大小进行升序排序
sort(candidate.begin(), candidate.end(), myCmp);
//获取差的绝对值最小的结果
long long diff = LLONG_MAX, longN = stoll(n);
string res = "";
for (auto& tempRes : candidate) {
long long tempDiff = abs(longN - stoll(tempRes));
if (tempDiff < diff) {
diff = tempDiff;
res = tempRes;
}
}
return res;
}
//自定义按照字符串数值大小排序
static bool myCmp(string & strOne, string & strTwo) {
if (strOne.size() == strTwo.size()) {
return strOne < strTwo;
}
else {
return strOne.size() < strTwo.size();
}
}
//判断是否是回文对称
bool isPalindromic(string & num) {
int numSize = num.size(), mid = numSize / 2;
for (int index = 0; index < mid; ++index) {
if (num[index] != num[numSize - 1 - index]) {
return false;
}
}
return true;
}
};
上 面 的 代 码 评 论 区 里 找 出 了 反 例 \color{red}上面的代码评论区里找出了反例 上面的代码评论区里找出了反例
在 55 − 80 行 中 处 理 存 在 漏 洞 , \color{red}在55-80行中处理存在漏洞, 在55−80行中处理存在漏洞,
对 于 0 没 有 进 行 − 1 操 作 , 对 于 9 没 有 进 行 + 1 操 作 。 \color{red}对于0没有进行-1操作,对于9没有进行+1操作。 对于0没有进行−1操作,对于9没有进行+1操作。
我们将字符串型修改为long long型进行加、减操作就快捷很多,并且将vector容器修改为set容器,这样就需要额外进行排序。
class Solution {
public:
string nearestPalindromic(string n) {
//set自动升序排序
set<long long> candidateSet;
long long N = stoll(n), strSize = n.size();
//添加10*01或10**01候选答案
candidateSet.insert(pow(10, strSize) + 1);
//添加9*9或9**9候选答案
candidateSet.insert(pow(10, strSize - 1) - 1);
//如果n的长度是奇数,则最中间的一个位进行加一、减一、不变操作再对称翻转得到三个候选结果
//比如奇数长度n=“12453”,把中间的4进行三个操作得到"12321"、“12421”、“12521”
//比如偶数长度n=“124553”,把中间的4进行三个操作得到"123321"、“124421”、“125521”
//注意偶数长度最中间有两个数位,我们取左边的,因为后面是将左半边的翻转到右半边
//截取n的前一半并转换为long long类型(相比于直接操作字符串更方便些)
//比如n=“12453”中的124,n=“124553”的124
long long preHalf = stoll(n.substr(0, (strSize + 1) / 2));
for (int i = -1; i <= 1; ++i) {
//获取左半加i的结果
string leftStr = to_string(preHalf + i);
//将左半翻转为右半边(如果n长度是奇数,比如n=“12453”,前左半翻转需要剔除最中间的,左半“124”,右半“21”)
string rightStr = string(leftStr.rbegin() + (strSize % 2), leftStr.rend());
//合并后转换为long long型放入候选集
candidateSet.insert(stoll(leftStr + rightStr));
}
//候选结果集中可能含有自身,需要剔除
candidateSet.erase(N);
//最后在结果集(set升序排列)中查找出最近的回文数
long long res = -1, diff = LLONG_MAX;
for (auto tempRes : candidateSet) {
long long tempDiff = abs(N - tempRes);
if (tempDiff < diff) {
diff = tempDiff;
res = tempRes;
}
}
return to_string(res);
}
};
修改为long long类型操作代码简洁了很多。