最接近的回文数
Closest Palindrome Number
我遇到了一个常见的面试问题,那就是找到最接近的回文数。假设如果输入为 127,则输出将为 131,如果输入为 125,则输出应为 121。
我可以想出逻辑,但我的逻辑在某些情况下失败,例如 91、911。在这些输入中,它给出99,919,但正确的输出是88和909。
算法步骤如下:
- 将数字转换为字符串。
- 以相反的顺序将前半部分复制到后半部分
- 转换为数字并测量 ABS。 与原始数字的差异 diff1
- 将 1 添加到一半字符串,现在以相反的顺序将前半部分复制到后半部分
- 转换为数字并测量 ABS。 与原始数字的差异 diff2
- 如果 diff1 小于 diff2,则返回第一个数字,否则返回第二个数字
这实际上是一个有趣的问题。显然,要使这不仅仅是一个蛮力,你想做的是使用最重要的数字并将它们放在最不重要的数字位置以形成回文。(我将回文和原文之间的差异称为"距离"(
由此,我要说的是,我们可以忽略数字中最不重要的一半,因为这真的无关紧要(在确定距离时很重要,但仅此而已(。
我要取一个抽象的数字:ABCDEF
.其中 A、B、C、D、E、F 都是随机数字。正如我所说,确定回文不需要 D,E,F,因为我们想要的是将数字的前半部分镜像到后半部分。显然,我们不想反过来做,否则我们会修改更有效的数字,导致与原始数字的距离更大。
所以回文是ABCCBA
,但是正如你已经说过的,这并不总是你最短的距离。然而,"解决方案"仍然是XYZZYX
的形式,所以如果我们考虑最小化我们正在修改的数字的"重要性",这意味着我们要修改 C(或最中间的数字(。
让我们退后一步,看看为什么:ABCCBA
- 起初,修改
A
可能很诱人,因为它处于最不重要的位置:最右边。但是,为了修改最不重要的,我们需要修改最重要的。所以A
出来了。 B
也是如此,所以C
最终成为我们选择的数字。
好的,既然我们已经确定要修改C
以获得可能更接近的数字,我们需要考虑边界。 ABCDEF
是我们原来的数字,如果ABCCBA
不是最接近的回文,那可能是什么?根据我们上面的小弯路,我们可以通过 修改C
.所以有两种情况,ABCDEF
大于ABCCBA
或小于ABCCBA
。
如果ABCDEF
大于 ABCCBA
则让我们将 1 加到 C
。我们会说T = C+1
所以现在我们有一些ABTTBA
.因此,我们将进行测试以确保ABCDEF - ABCCBA > ABCDEF - ABTTBA
如果是这样,我们知道ABTTBA
是最接近的回文。因为对 C 的任何更多修改只会让我们越来越远。
或者,如果ABCDEF
小于 ABCCBA
,那么我们将从 C
中减去 1。比方说V = C-1
.所以我们有 ABVVBA
,就像上面一样,我们将进行测试:ABCDEF - ABCCBA > ABCDEF - ABVVBA
,您将拥有相同的解决方案。
诀窍是ABCDEF
总是在 ABTTBA
和 ABVVBA
之间,而这些数字之间唯一的其他回文是 ABCCBA
.因此,您只有 3 个解决方案选项。如果将ABCDEF
与ABCCBA
进行比较,则只需检查 2。
我认为你不难适应任何大小的数字。 在奇数位数的情况下,你只需要ABCBA
、ABVBA
和ABTBA
等等......
所以就像你的例子一样:让我们以911为例。
- 忽略最后 1 我们只取前半部分(四舍五入(。 所以91X。
- 将 X 替换为 9。我们有 919。这是中点。
- 我们知道我们原来的 911 小于 919,所以从我们的中间数字中减去 1,所以我们得到第二个(下限(909。
- 比较
911 - 919
和911 - 909
- 返回差异最小的那个。
因此,这为我们提供了一个恒定时间算法:)正如评论中指出的那样,在最坏的情况下(哎呀(,这不是恒定的时间,但肯定比蛮力方法更好。
您所拥有的,但我想我会详细说明希望阐明这个问题,否则这似乎是您的一个小编程错误。
这是Naveen和Don算法的实现。它使用Happy Yellow Face的算法作为测试预言机。
我很高兴看到人们调整它以删除多余的步骤或特殊情况。
gcc 4.7.3: g++ -Wall -Wextra -std=c++0x nearest-palindrome.cpp
#include <algorithm>
#include <cassert>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include <vector>
// I do not have std::to_string.
template <class T>
std::string to_string(const T& v) {
std::stringstream ss;
ss << v;
return ss.str(); }
// Nor do I have std::stoi. :(
int stoi(const std::string& s) {
std::stringstream ss(s);
int v;
ss >> v;
return v; }
bool isPalindrome(int n) {
const auto s = to_string(n);
return s == std::string(s.rbegin(), s.rend()); }
int specNearestPalindrome(int n) {
assert(0 <= n);
int less = n, more = n;
while (true) {
if (isPalindrome(less)) { return less; }
if (isPalindrome(more)) { return more; }
--less; ++more; } }
std::string reflect(std::string& str, int n) {
std::string s(str);
s.resize(s.size() + n);
std::reverse_copy(std::begin(str),
std::next(std::begin(str), n),
std::next(std::begin(s), str.size()));
return s; }
bool isPow10(int n) {
return n < 10 ? n == 1 : (n % 10 == 0) && isPow10(n / 10); }
int nearestPalindrome(int n) {
assert(0 <= n);
if (n != 1 && isPow10(n)) { return n - 1; } // special case
auto nstr = to_string(n);
// first half, rounding up
auto f1 = nstr.substr(0, (nstr.size() + 1) / 2);
auto p1 = stoi(reflect(f1, nstr.size() / 2));
const auto twiddle = p1 <= n ? 1 : -1;
auto f2 = to_string((stoi(f1) + twiddle));
auto p2 = stoi(reflect(f2, nstr.size() / 2));
if (p2 < p1) { std::swap(p1, p2); }
return n - p1 <= p2 - n ? p1 : p2; }
int main() {
std::vector<int> tests = { 0, 1, 6, 9, 10, 11, 12, 71, 74, 79, 99, 100, 999, 1000, 9900, 9999, 999000 };
for (const auto& t : tests) {
std::cout <<
(nearestPalindrome(t) == specNearestPalindrome(t) ? "." : "X");
}
std::cout << std::endl;
return 0; }
这是一个通用算法,尽管使用暴力破解,但可以工作1:
int findNearestPalindrome(int n) {
int less = n;
int more = n;
while(true) {
if (isPalindrome(less)) return less;
if (isPalindrome(more)) return more;
--less;
++more;
}
}
在isPalindrome()
函数中,您需要做的就是将数字转换为字符串,然后将字符串与自身颠倒进行比较。
1然而,这不会像泰德·霍普评论的那样检查平局情况。您必须进行一些更改才能使其可识别。
#include <iostream>
#include <cmath>
#include <functional>
#include <limits>
#include <sstream>
// for convience
using namespace std;
using ULL = unsigned long long int;
// calculate the number of digits
auto Len = [](auto num) -> ULL {
return floor(log10(num)) + 1; };
// extract left half of number
auto Halfn = [](auto num, auto olen) {
for (unsigned i = 0; i < olen / 2; num /= 10, ++i);
return num;
};
int main() {
ULL num; cin >> num;
// some basic checking
if (num < 10) {
cerr << "Error, enter a number >= 10";
return 0;
}
if (numeric_limits<ULL>::max() < num) {
cerr << "Error, number too largen";
return 0;
}
cout << ([](auto num) {
auto olen = Len(num);
auto lhalf = Halfn(num, olen);
function<ULL(ULL)> palin = [olen] (auto lhalf) {
auto half = to_string(lhalf);
// this is the mirror string that needs to be
// appended to left half to form the final
// palindrome
auto tmp = half.substr(0, olen / 2);
// take care of a corner case which
// happens when the number of digits in
// the left half of number decrease, while
// trying to find a lower palindrome
// e.g. num = 100000
// left half = 100 , the value passed to the
// function palin, is 99. if all digits are 9
// then we need to adjust the count of 9,
// otherwise if i simply replicate it, i'll get
// 9999 but one more 9 is required for the
// correct output.
if (olen / 2 > tmp.size() &&
all_of(tmp.begin(), tmp.end(),
[](auto c) { return '9' == c; })) {
tmp += '9';
}
// append, convert and return
half = half + string(tmp.crbegin(),
tmp.crend());
return stoull(half);
};
auto bpalin = palin(lhalf);
auto hpalin = palin(lhalf + 1);
auto lpalin = palin(lhalf - 1);
stringstream ss;
ss << "base palindrome = " << bpalin <<'n';
ss << "higher palindrome = "<<hpalin <<'n';
ss << "lower palindrome = " << lpalin <<'n';
// calculating absolute difference for
// finding the nearest palindrome
auto diffb = labs(bpalin - num);
auto diffh = labs(hpalin - num);
auto diffl = labs(lpalin - num);
auto nearest = (diffb < diffh) ?
(diffb < diffl) ? bpalin : lpalin :
(diffh < diffl) ? hpalin : lpalin;
ss << "nearest palindrome = "
<< nearest << endl;
return move(ss.str());
}(num));
} // end main
class Solution {
public String nearestPalindromic(String n) {
int order = (int) Math.pow(10, n.length()/2);
Long ans = Long.valueOf(new String(n));
Long noChange = mirror(ans);
Long larger = mirror((ans/order)*order + order+1);
Long smaller = mirror((ans/order)*order - 1 );
if ( noChange > ans) {
larger = (long) Math.min(noChange, larger);
} else if ( noChange < ans) {
smaller = (long) Math.max(noChange, smaller);
}
return String.valueOf( ans - smaller <= larger - ans ? smaller :larger) ;
}
Long mirror(Long ans) {
char[] a = String.valueOf(ans).toCharArray();
int i = 0;
int j = a.length-1;
while (i < j) {
a[j--] = a[i++];
}
return Long.valueOf(new String(a));
}
}
Javascript 解决方案:
const findNearestPalindrome = n => {
if (!n) return 0;
let lowestPalindorm = lowestPalindromeHelper(n);
let largestPalindrome = largestPalindromeHelper(n);
let closestPalindrome = 0;
closestPalindrome =
Math.floor(n - lowestPalindorm) > Math.floor(largestPalindrome - n)
? largestPalindrome
: lowestPalindorm;
console.log(closestPalindrome);
};
//lowestPalindrome check
const lowestPalindromeHelper = num => {
for (let i = num - 1; i >= 0; i--) {
if (isPalindrome(i.toString())) {
return i;
}
}
};
//largest Palindrome Check
const largestPalindromeHelper = num => {
for (let i = num + 1; i <= Number.MAX_SAFE_INTEGER; i++) {
if (isPalindrome(i.toString())) {
return i;
}
}
};
const isPalindrome = n => {
return (
n ===
n
.split('')
.reverse()
.join('')
);
};
findNearestPalindrome(1234);
- 查找最接近的大于当前数字的数字的索引
- C++:正在检查LinkedList中的回文-递归方法-错误
- Usaco第1.6节主要回文
- 必须首先打印向量 v1 中最接近整数 x 的数字<int>
- 最大的回文产品 - 程序未运行,编写解决方案但无法理解问题
- 将 S1 转换为回文,并将 S2 作为其子字符串
- 最长的回文子串(C++帮助)
- 整数数组中最长的回文
- 最大的回文产品(Project Euler)——C++
- 如何创建一个程序来标识最长的子字符串回文,而不考虑字母大小写
- 如果有一个以上的allindromes,找到最长但词典最小的回文
- 加速C++以找到最长的回文
- 字符串中的最小回文计数
- 找到下一个最大的回文数
- 最接近的回文数
- 求两个3位数乘积的最大回文数
- 获取最大回文
- 最大回文数3位
- 最大的两位数回文
- DP方法,用于生成字符串回文时应添加的最小字符数