基本思路
写两个函数
一个函数 判断素数,判断思路素数较为简单,直接循环遍历1-根号n,判断n%i是否等于0就好了
bool iss(int n){
if(n==1) return false;
for(int i=2;i*i<=n;i++){
if(n%i==0) return false;
}
return true;
}
另一个判断回文数,开一个数组存储n的各个位数,然双指针遍历数组首位是否相等
bool isPalindrome(int n)
{
if(n<10) return true;
int temp=n,j=0;
while(temp>0) {
j++;
temp/=10;
}
int a[j];
temp=n;
for(int i=0;i<j;i++){
a[i]=temp%10;
temp/=10;
}
j=j-1;
for(int i=0;i<=j;){
if(a[i]!=a[j]) {
return false;
}
i++; j--;
}
return true;
}
如果这样直接提交而不优化,毫无疑问肯定会TLE
那么我们可以从两个函数出发,我们知道我们要在i>=n寻找,那么如果这个数是偶数,必然不是素数,我们可以跳过。
int primePalindrome(int n){
if(n==1) return 2;
if(n<10&&iss(n)) return n;
for(int i=n;i<200000000;i++){
if(i%2==0) continue;
if(isPalindrome(i)&&iss(i)) return i;
}
return 0;
}
依旧TLE
自行输出各个区间段回文素数可以发现10000000到100000000是没有素数的,加上限制条件
(这也是后来看了题解才了解到
if ((i > 10000000) && (i < 100000000)) {
i = 100000000;
}
暴力便可以通过,但beat率依旧不美观
bool iss(int n){
if(n==1) return false;
for(int i=2;i*i<=n;i++){
if(n%i==0) return false;
}
return true;
}
bool isPalindrome(int n)
{
if(n<10) return true;
int temp=n,j=0;
while(temp>0) {
j++;
temp/=10;
}
int a[j];
temp=n;
for(int i=0;i<j;i++){
a[i]=temp%10;
temp/=10;
}
j=j-1;
for(int i=0;i<=j;){
if(a[i]!=a[j]) {
return false;
}
i++;
j--;
}
return true;
}
int primePalindrome(int n){
if(n==1) return 2;
if(n<10&&iss(n)) return n;
for(int i=n;i<200000000;i++){
if ((i > 10000000) && (i < 100000000)) {
i = 100000000;
}
if(i%2==0||i%3==0) continue;
if(isPalindrome(i)&&iss(i)) return i;
}
return 0;
}
到这里想必也有不少人和我一样无数次TLE,不要气馁要懂得吸收题解,为己所用
我稍微讲解一下这个思路吧 重点在于如何从回文数字求下一个回文数字
既然从扫描数字上时间效率太低那么我们就从回文数字上找,构造回文数字我们可以从给定的参数N来构造 考虑到奇数位数,偶数位数,我们直接举例子123456,1234567.
注意这里存储时倒序
while(N){
nums[index++] = N % 10;
N /= 10;
}
转换成最接近的回文数字我们直接把数组后面的位置往前搬,得到的数字小于等于N
如果是奇数数位
具体实现代码
for( ; m < n; ++m, --n) nums[m] = nums[n]; // 变成回文数组
注意如果数组奇数位数 最终n=m,上面for的过程 我们图解可以发现在3迁移到前面去时n左移,m右移,n=m;
而如果偶数位数最终,m-n=1;
然后我们要让这个会问数字增加,如果是奇数位则怎加中间的数位,如果数位大于9则要向两边进位过程
图解 4进位到9 到向两边 (奇数位)
偶数位
那么代码如何实现呢(重点)
首先难点在于中间位置的处理,我们在首次求出回文数组时,由前面我们确定了
奇数位数 n=m 中间位置 n
偶数位数 m-n=1; 中间位置n m
有两种情况 nums[i]<9 那么直接+1,nums[i]==9 进位
处理进位,那么我们需要从数组中间向两边遍历 ,如果逐次进位,对应n左右两边位数加一
奇数位数 nums[pos_1] = nums[index-1-pos_1] = 0; pos_1=index-1-pos_1
偶数位数 pos_1 index-1-pos_1 位置对称
如果小伙伴们不明白可以看图模拟一下
if(nums[n] == 9){ //如果中间一位或者两位为9
int pos_1 = n;
while(pos_1 >= 0 && nums[pos_1] == 9){
nums[pos_1] = nums[index-1-pos_1] = 0;
pos_1--; //进位
}
if(pos_1 < 0){ //如果达到当前数量级的最大值(99...99)
int j = 0;
n = index;
index++;
for( ; j < n; --n, ++j)
nums[n] = nums[j] = 0;
nums[0] = nums[index - 1] = 1; // 置为(10...01)
}
else{ //否则将该位置以及对应回文位置加一
nums[pos_1]++;
nums[index - pos_1 - 1]++;
}
}
else{ // 如果不是9,则该位置和对应回文位置都加一
int number = nums[n];
nums[n] = number + 1;
nums[index-n-1] = number + 1;
}
}
解决了难点只需要求出回文数组后将其转化成数字然后判断素数
总代码:
bool check(int cur){
for(int i = 2; i <= sqrt(cur) + 1; ++i)
if(cur % i == 0) return false;
return true;
}
int primePalindrome(int N){
if(N <= 2) return 2;
int nums[32];
int index = 0;
int pos = N;
while(N){
nums[index++] = N % 10;
N /= 10;
}
int m = 0, n = index - 1;
for( ; m < n; ++m, --n)
nums[m] = nums[n]; // 变成回文数组
while(1){
int cur = 0;
for(int i = 0; i < index; ++i)
cur = cur * 10 + nums[i];
if(cur >= pos && check(cur))
return cur; //如果是素数,返回
else{
if(nums[n] == 9){ //如果中间一位或者两位为9
int pos_1 = n;
while(pos_1 >= 0 && nums[pos_1] == 9){
nums[pos_1] = nums[index-1-pos_1] = 0;
pos_1--; //进位
}
if(pos_1 < 0){ //如果达到当前数量级的最大值(99...99)
int j = 0;
n = index;
index++;
for( ; j < n; --n, ++j)
nums[n] = nums[j] = 0;
nums[0] = nums[index - 1] = 1; // 置为(10...01)
}
else{ //否则将该位置以及对应回文位置加一
nums[pos_1]++;
nums[index - pos_1 - 1]++;
}
}
else{ // 如果不是9,则该位置和对应回文位置都加一
int number = nums[n];
nums[n] = number + 1;
nums[index-n-1] = number + 1;
}
}
}
return 0;
}
不得不说效率真的很高
不要懊恼与写不出题,重要在于收获!!!