(第七天)回文素数字

 

跟着英雄哥一起光天化日学算法

866. 回文素数

基本思路

写两个函数

一个函数 判断素数,判断思路素数较为简单,直接循环遍历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,不要气馁要懂得吸收题解,为己所用

c语言题解中较为出色的一篇

我稍微讲解一下这个思路吧 重点在于如何从回文数字求下一个回文数字

既然从扫描数字上时间效率太低那么我们就从回文数字上找,构造回文数字我们可以从给定的参数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;
}

不得不说效率真的很高

不要懊恼与写不出题,重要在于收获!!!

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值