您的位置:首页 > 职场人生

Google面试题:计算从1到n的正数中1出现的次数

2014-08-20 19:47 344 查看
题目:

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

  找工作,准备看写题目,题目说是Google面试题,遂很认真地自己做了下。

找规律:

  其实可以从中找出数列的规律。求从1到n数字中的1共有多少个,会想到按照数字的位数来观察观察,比如1位数字里(从1到9)共有1个,记W[1]=1;2位数字里(01到99)共有W[2]个,3位数字(001到999)共有W[3]个,定义如下数组:

enum{N = 4};
int W
;        //数组含义:下标 i 代表整数位数是 i 的共有包含多少个 1,如 i 是 2 代表[1, 99]区间全部整数共包含多少个 1


要是把这个数组的值全部求到了,那么就好办了。下面找这个数组的规律。

  先看看W[3]与W[2]的关系,如下图:

  

#include <iostream>
#include <sstream>
#include <string>
#include <cmath>
#include <cstdlib>
#include <fstream>
#include <ctime>

using std::cout;
using std::endl;
using std::cin;
using std::string;

//最原始的方法,即从1到n,看每个数字中的“1”的个数,然后加到一起
int getCntSimple(int n)
{
int cnt = 0;
for (int i = 1; i <= n; i++)
{
//数字到字符串的转换
std::stringstream ss;
ss << i;
string temp = ss.str();

int thisCnt = 0;
for (int j = 0; j < temp.size(); j++)
{
if ('1' == temp[j])
thisCnt++;
}

cnt += thisCnt;
}
return cnt;
}

//较好的方法
enum{N = 5};
int W
;        //数组含义:下标 i 代表整数位数是 i 的共有包含多少个 1,如 i 是 2 代表[1, 99]区间全部整数共包含多少个 1

//初始化W数组
void initWArr(void)
{
W[0] = 0;
for (int i = 0; i < N - 1; i++)
{
W[i + 1] = W[i] * 10 + std::pow(10, i);
}
}

int getCntBetter(int n)
{
//数字转成字符串
std::stringstream ss;
ss << n;
string temp = ss.str();

int len = temp.size(), res = 0;

//其实这里的每次循环处理的都是最大的一位,以后的都相当于零头子
for (int i = 0; i < len; i++)
{
int num = (temp[i] - '0');        //由字符转为数字

if (num > 1)        //注意当前这一位是不是比1大
{
res += num * W[len - i - 1] + std::pow(10, len - i - 1);        //比1大就直接加上10^(len - i - 1)个,完整的那么多次方个,完全贡献
}
else if (0 == num)        //如果这个数是0
{
continue;        //直接进行下一次循环
}
else    //这数字是1,下个循环要去掉这个位,得先把这个1的贡献算上
{
if (i < len - 1)            //不是最后一位的1
{
//获取这个1后面的数字
string strVal = temp.substr(i + 1);
std::stringstream ss(strVal);
int val;
ss >> val;

res += W[len - i - 1] + (val + 1);
}
else     //最后一位了,且是1
{
res += 1;
}
}
}

return res;
}

int main(void)
{
initWArr();
std::ofstream fout("src.txt");

/*
for (int i = 0; i < 2000; i++)
{
int n =std::rand() % 1000;
int a = getCntSimple(n);
int b = getCntBetter(n);
fout << n << "   :" << a << "  " << b << endl;
cout << i << ((a == b) ? " 是" : " 否") << endl;
}*/

clock_t aTimeStart = std::clock();
for (int i = 0; i < 2000; i++)
{
int a = getCntSimple(i);
}
clock_t aTimeEnd = std::clock();
cout << "runing time a: " << static_cast<double>(aTimeEnd - aTimeStart) / CLOCKS_PER_SEC * 1000 << "ms" << endl;

clock_t bTimeStart = std::clock();
for (int i = 0; i < 2000; i++)
{
int a = getCntBetter(i);
}
clock_t bTimeEnd = std::clock();
cout << "runing time b: " << static_cast<double>(bTimeEnd - bTimeStart) / CLOCKS_PER_SEC * 1000 << "ms" << endl;

cin.get();
}


View Code
  多多注意从第61行开始的判断:

(1)如果这个数字是大于1的,那么就类似于求W数组的那种方式;

(2)这个数字是0,那么直接跳过,没有贡献;

(3)这个数字是1的话,注意,如果这个1已经是最后一位了,那么直接加一个1就好了。但是如果不是的话,假设这是数字是123了,那么想要去掉这个1直接处理后面的零头23时,[1, 99]可以用W[2]获得,但是这个一对后面的24个数字(00到23)都有影响,所以要加上这么多个1。

可以运行试试。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: