从1到n整数中1出现的次数

题目

输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。例如输入12,从1到12这些整数中包含1的数字有1,10,11和12,1一共出现了5次。

思路

  1.先根据数组的最高位计算1出现的次数 

  • 如果只有一位数且该为是0,返回0 
  • 如果只有一位数且该为大于0,则1出现的次数只有1次
  • 如果最高位数字大于1,则最高位1出现的次数是10^len-1次方 
  • 如果最高位数字不大于1,1出现的次数是除最高位所有的数字+1次 

  2.其他位1出现的次数:最高位不变,固定其他一位,剩余的位数全排列 

  3.去除最高位,递归求得依次剩余的位数 

#include <iostream>
#include <sstream>
#include <cmath>
using namespace std;

class Solution
{
    public:
        int num_of_one(string s);
        int digit(const unsigned int n);
};
int Solution::num_of_one(string s)
{
    if(s.empty()||s.size()<=0||*s.begin()<='0'||*(--s.end())>'9')
        return 0;
    
    //1.先根据数组的最高位计算1出现的次数 
    int first=*s.begin()-'0';
    unsigned int len=s.size();
    if(len==1&&first==0) //如果只有一位数且该为是0,返回0 
        return 0;
    else if(len==1&&first>0)//如果只有一位数且该为大于0,则1出现的次数只有1次 
        return 1;
    
    int num_first_digit=0;
    if(first>1)//如果最高位数字大于1,则最高位1出现的次数是10^len-1次方 
        num_first_digit=digit(len-1);
    else if(first==1)//如果最高位数字不大于1,1出现的次数是除最高位所有的数字+1次 
        num_first_digit=*(++s.begin())-'0'+1;
    
    //2.其他位1出现的次数:最高位不变,固定其他一位,剩余的位数全排列 
    int num_other_digit=first*(len-1)*digit(len-2);
    s.erase(s.begin());
    //3.去除最高位,递归求得依次剩余的位数 
    int num_recursive=num_of_one(s);
    
    return num_first_digit+num_other_digit+num_recursive;
}
int Solution::digit(const unsigned int n)
{
    return (int)pow(10,n);
} 
int main()
{
    long n;
    cin>>n;
    
    stringstream ss;
    ss<<n; 
    Solution s;
    cout<<s.num_of_one(ss.str())<<endl;
    return 0;
}

 二

  对于个位数,N中1出现的次数不仅和N的个位数有关,还和N的十位数有关

    1.如果个位数大于1,则个位数1出现的次数=十位数字+1;

    2.如果个位数字为0,则个位数1出现的次数=十位数字;

  对于十位数字,1出现的次数不仅和十位数有关,还和个位数有关

    1.如果十位数字=1,则十位1出现的次数=个位数字+1;

    2.如果十位数字>1,则十位1出现的次数=10;

  对于百位数字,设定整数点(如1、10、100等等)作为位置点i(对应n的各位、十位、百位等等),分别对每个数位上有多少包含1的点进行分析。

  1. 根据设定的整数位置,对n进行分割,分为两部分,高位n/i,低位n%i
  2. 当i表示百位,且百位对应的数>=2,如n=31456,i=100,则a=314,b=56,此时百位为1的次数有a/10+1=32(最高两位0~31),每一次都包含100个连续的点,即共有(a/10+1)*100个点的百位为1
  3. 当i表示百位,且百位对应的数为1,如n=31156,i=100,则a=311,b=56,此时百位对应的就是1,则共有a/10(最高两位0-30)次是包含100个连续点,当最高两位为31(即a=311),本次只对应局部点00~56,共b+1次,所有点加起来共有(a/10*100)+(b+1),这些点百位对应为1
  4. 当i表示百位,且百位对应的数为0,如n=31056,i=100,则a=310,b=56,此时百位为1的次数有a/10=31(最高两位0~30),所以加起来公(a/10)*100个点

所以,当百位对应0或>=2时,有(a+8)/10次包含所有100个点,还有当百位为1(a%10==1),需要增加局部点b+1

#include <iostream>
using namespace std;

class Solution
{
    public:
        int num_of_one(long n);
};
int Solution::num_of_one(long n)
{
    if(n<=0)
        return 0;
    
    long sum=0;//1的总个数
    long base=1;//当前位
    long low_num=0;//低位数字
    long heigh_num=0;//高位数字
    long curr_num=0;//当前位数字
    
    while(n/base!=0)
    {
        low_num=n-(n/base)*base;
        curr_num=(n/base)%10;
        heigh_num=n/(base*10);
        
        if(curr_num==0) 
            sum+=heigh_num*base;
        else if(curr_num==1)
            sum+=heigh_num*base+low_num+1;
        else
            sum+=(heigh_num+1)*base;
            
        base*=10;
    }
    return sum;
}

int main()
{
    long n;
    cin>>n;
        
    Solution s;
    cout<<s.num_of_one(n)<<endl;
    return 0;
}

 

转载于:https://www.cnblogs.com/tianzeng/p/10241808.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值