2013年大众点评网笔试题,输入任意正整数n,统计1到n中1出现的次数

输入任意正整数n,统计1到n中1出现的次数,比如输入12,其中1,10,11,12出现了5次。

 

很容易会想到的方法是遍历1~n,然后逐位计算1的个数。这是同学写的一个代码。

 

#include <iostream>  
#include <sstream>
#include <string>
using namespace std;
void main(){
int n,count = 0;
cin>>n;
for(int i = 1;i <= n;i++){
string nStr = to_string(i);
int pos = nStr.find_first_of('1');
if( pos != string::npos) {
count++;
for(;nStr.find_first_of('1',pos + 1) != string::npos; count++, pos = nStr.find_first_of('1', pos + 1));
}
}
cout<<count<<endl;
}


很明显这种算法的时间复杂度为O(n)。但如果我们把n拆开逐位看待的话,就会发现其实1出现的次数是有规律的,而且很大部分是取决于这一位前面的数值,比如1234,对个位来说,遍历的时候每10次个位就会出现一次,因而个位起码出现了123(个位前面的数值)*10次,再加上个位数4比1大,再加上一次,个位一共出现1231次1;同理,对十位来说,每遍历100,十位就会出现10次1,因而十位至少出现12*10次1,再加上十位和个位上的数31比19大,因而再加上10~19这10次,总共出现130次(若十位为1则需要个位的数值,比如十位个位为12则记出现3次,同理可得);其他位也是同样的情况。这种算法需要循环的次数仅为n的位数长度,时间复杂度为O(1)。参考代码如下。

#include <iostream>  
using namespace std;

void main()
{
int n,length=0,sum=0;
cin>>n;
int number=n;
while(number>0)//计算n的长度
{
number/=10;
length++;
}
for(int i=1;i<=length;i++)//从个位开始计算每个位上面1出现的次数
{
sum+=(int)(n/pow(10,i))*pow(10,i-1);//如1326,对个位(先不考虑个位情况下)出现1共132次,对十位(先不考虑十位情况下)1出现13*10=130次
int tmp=(int)(n/pow(10,i-1))%10;//取出本位上的数字,如1326,个位为6,十位为2等
if(tmp>1)
sum+=pow(10,i-1);//若此位大于1,则此位为1时所有情况都需要考虑上
if(tmp==1)
sum+=n%(int)(pow(10,i-1))+1;//若此位等于1情况,如1112,对十位,需要加上10,11,12,三种十位为1的情况,对百位加上100~112这13种百位为1的情况
}
cout<<sum<<endl;
}

 

比较两种算法的运行时间如下:


 
明显第二种算法时间少很多。
 
同时,这种方法也可以统计多位数的出现情况,如统计‘23’出现的次数就考虑每100出现一次‘23’即可。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值