问题陈述
如果一个非负整数 𝑋X 的十进制表示(不含前导零)是一个回文数,那么这个非负整数 𝑋X 就叫做回文数。
例如, 363363 、 1234432112344321 和 00 都是回文数。
求 𝑁N /th最小的回文数。
限制因素
- 1≤𝑁≤10181≤N≤1018
- 𝑁N 是整数。
做法
我们构造回文串,只需要知道前半段的回文串长啥样,就可以构造出一整个回文串。
再看看回文串的长度,长度为1时,有10种构造方式(0到9);长度为2时,有9种构造方式(只考虑回文串的前半段,且没有前导0);长度为3时,构造回文串的前两个位,第一位有九种选择(1到9),第二位有10种(0到9)…… 长度为6时,构造前三位,第一位有九种选择,第二位有十种选择,第三位有十种选择。
我们发现,若一个cnt位的回文串,我们需要构造出前(cnt+1)/ 2 位,共有9*10^((cnt+1)/2-1) 种不同的回文串(第一位是9种选择,其他位都是10种选择)。这样我们就能知道第n小的回文数是有几位数构成的了。
那么我们知道了他是几位数构成的,我们又怎么构造出这个回文串呢?我们假设第n大的回文数是由cnt位数字构成的,k是他的前半段的位数,即(cnt+1)/ 2 。那么在cnt位的回文数中,前半段回文数是1*10^(k-1) 的是第一小的回文数,1*10^(k-1)+1是第二小的回文数……这个就是可以用1*10^(k-1)+n-1推出来(n在前面处理时,已经减去了前面1到cnt-1位回文数的个数)。
#include<bits/stdc++.h>
using namespace std;
long long n;
long long ksm(long long a,long long b){long long ans=1;while(b){if(b%2) ans*=a;b/=2;a*=a;}return ans;
}
int main() {scanf("%lld",&n);if(n==1){cout<<0;return 0;}n--;long long cnt=1;//位数long long k=(cnt+1)/2;//前面一半long long res=9*ksm(10,k-1);//位数对应的回文串个数 while(n>res){//找出第n小的回文数的位数n-=res;cnt++;k=(cnt+1)/2;res=9*ksm(10,k-1);}long long ans=ksm(10,k-1)+n-1;//求出前半段string a=to_string(ans);string b=a;if(cnt%2==0){//回文串位数为偶数reverse(a.begin(),a.end());b+=a;}else{//回文串位数为奇数string c=a.substr(0,a.size()-1);reverse(c.begin(),c.end());b+=c;}cout<<b;
}