题目:

  输入一个整数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 = };
int W[N]; //数组含义:下标 i 代表整数位数是 i 的共有包含多少个 1,如 i 是 2 代表[1, 99]区间全部整数共包含多少个 1

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

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

  

上图左边是一个三位数的第一位的所有情况,即从0到9,右边是后两位的所有情况,从00到99,这就是从000到999所有数字的情况了。注意:对于一个三位数,左边是10种情况,即从0到9,右边的每个从00到99包含的1的个数不就是W[2]吗?再来看三位数字当左边一位是1的情况,这时,数字为从100到199,这100一个数字每个最左边都是一个1。综上:W[3] = 10 * W[2] + 100

  同理可以分析W[4] = 10 * W[3] + 1000,这后面加的100、1000其实就是10的(位数-1)次方。我们猜测:W[n+1] = 10 * W[n] + 10^n(n>=0,W[0]=0)。可以当做数列证明的。




运算:

  找到规律就好办了,一下子就可以把W数组求出来了。但是对于一个数字呢?比如768,如下:

一:先算从000到699的个数,不就 7 * W[2] + 100 嘛,道理同上。

二:再算从700到768,因为最前面的 7 已经不贡献 1 了,所以这个相当于00到68了,其中00到59是 6 * W[1] + 10。

三:剩下60到68,前面6也不贡献了,相当于0到8,就一个。所以加起来就完了。

  注意:万一是168,第一步就不对了,1 * W[2] 后就不能加 100 了,这种情况要注意处理好,下面直接上代码,注释很详细啦:

 #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 = ;
for (int i = ; i <= n; i++)
{
//数字到字符串的转换
std::stringstream ss;
ss << i;
string temp = ss.str(); int thisCnt = ;
for (int j = ; j < temp.size(); j++)
{
if ('' == temp[j])
thisCnt++;
} cnt += thisCnt;
}
return cnt;
} //较好的方法
enum{N = };
int W[N]; //数组含义:下标 i 代表整数位数是 i 的共有包含多少个 1,如 i 是 2 代表[1, 99]区间全部整数共包含多少个 1 //初始化W数组
void initWArr(void)
{
W[] = ;
for (int i = ; i < N - ; i++)
{
W[i + ] = W[i] * + std::pow(, i);
}
} int getCntBetter(int n)
{
//数字转成字符串
std::stringstream ss;
ss << n;
string temp = ss.str(); int len = temp.size(), res = ; //其实这里的每次循环处理的都是最大的一位,以后的都相当于零头子
for (int i = ; i < len; i++)
{
int num = (temp[i] - ''); //由字符转为数字 if (num > ) //注意当前这一位是不是比1大
{
res += num * W[len - i - ] + std::pow(, len - i - ); //比1大就直接加上10^(len - i - 1)个,完整的那么多次方个,完全贡献
}
else if ( == num) //如果这个数是0
{
continue; //直接进行下一次循环
}
else //这数字是1,下个循环要去掉这个位,得先把这个1的贡献算上
{
if (i < len - ) //不是最后一位的1
{
//获取这个1后面的数字
string strVal = temp.substr(i + );
std::stringstream ss(strVal);
int val;
ss >> val; res += W[len - i - ] + (val + );
}
else //最后一位了,且是1
{
res += ;
}
}
} 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 = ; i < ; i++)
{
int a = getCntSimple(i);
}
clock_t aTimeEnd = std::clock();
cout << "runing time a: " << static_cast<double>(aTimeEnd - aTimeStart) / CLOCKS_PER_SEC * << "ms" << endl; clock_t bTimeStart = std::clock();
for (int i = ; i < ; i++)
{
int a = getCntBetter(i);
}
clock_t bTimeEnd = std::clock();
cout << "runing time b: " << static_cast<double>(bTimeEnd - bTimeStart) / CLOCKS_PER_SEC * << "ms" << endl; cin.get();
}

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

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

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

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

可以运行试试。

Google面试题:计算从1到n的正数中1出现的次数的更多相关文章

  1. 【google面试题】求1到n的正数中1出现的次数的两种思路及其复杂度分析

    问题描写叙述: 输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数.比如输入12,从1到12这些整数中包括1 的数字有1.10.11和12.1一共出现了5次. 这是一道广为流传的googl ...

  2. 【面试题032】从1到n整数中1出现的次数

    [面试题032]从1到n整数中1出现的次数 题目:     输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数.     例如输入12,从1到12这些整数中包含1的数字有1,10,11和1 ...

  3. 面试题32:从1到n整数中1出现的次数

    这是一道广为流传的google面试题.用最直观的方法求解并不是很难,但遗憾的是效率不是很高:而要得出一个效率较高的算法,需要比较强的分析能力,并不是件很容易的事情.当然,google的面试题中简单的也 ...

  4. 【剑指offer】面试题32:从1到n整数中1出现的次数

    题目: 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了.A ...

  5. 面试题四十三:在1~n整数中1出现的次数

    方法一:直观来看,遍历1到n,每个数去做%10的循环判断 int Number1_B_1toN( int n){ int sum=0; for(int i=1;i<=n;i++){ int k= ...

  6. (剑指Offer)面试题32:从1到n整数中1出现的次数

    题目: 输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数.例如输入12,从1到12这些整数中包含1的数字有1,10,11和12,一共出现了5次. 思路: 1.累加法 累加1到n中每个整数 ...

  7. 数组中第K小的数字(Google面试题)

    http://ac.jobdu.com/problem.php?pid=1534 题目1534:数组中第K小的数字 时间限制:2 秒 内存限制:128 兆 特殊判题:否 提交:1120 解决:208 ...

  8. Google 面试题和详解

    Google的面试题在刁钻古怪方面相当出名,甚至已经有些被神化的味道.这个话题已经探讨过很多次,而科技博客 BusinessInsider这两天先是贴出15道Google面试题并一一给出了答案,其中不 ...

  9. Google面试题(选自公众号)

    date: 2018-12-14 13:34:56 updated: 2018-12-14 13:34:56 Google面试题(选自公众号) 问题 把你的手机拨号页想象成一个棋盘.棋子走只能走&qu ...

随机推荐

  1. javascript实现单例模式

    1.简单实现单例模式: var singleTon = function(){ var _pria = 'private value'; var show_pria = function(){ con ...

  2. 排序命令sort

    Unix和Linux自带的sort命令功能非常强大,其主要功能是对文本内容按不同的方法排序.它不仅可以按一个或多个字段排序,还可以合并文件.使用sort处理一些较大的文件时,可能处理速度会比较慢,但却 ...

  3. GAC(Global Assembly Cache)注册/卸载 dll

    当发现有多个解决方案引用一个dll时,为了不重复引用所以将.net的一个dll注册到GAC中去. gacutil.exe. 记得使用管理员权限打开 开始菜单-Microsoft Visual Stud ...

  4. Golang学习 - unicode 包

    ------------------------------------------------------------ const ( MaxRune = '\U0010FFFF' // Unico ...

  5. Nice是如何做iOS客户端架构的?

    一个创业产品的iOS客户端架构到底怎么做呢?现下最有活力的图片社交软件Nice的技术负责人刘诗彬将为我们解答创业产品如何实现iOS客户端架构. 分享人:刘诗彬,毕业于北京邮电大学电子信息科学与技术专业 ...

  6. Android 之 权限 uses-permission 设置

    Manifest.permission 官方API说明: http://developer.android.com/reference/android/Manifest.permission.html ...

  7. star

    Astronomers often examine star maps where stars are represented by points on a plane and each star h ...

  8. Android 常用 adb 命令

    查看原文:http://blog.csdn.net/u010818425/article/details/52266593 (一)基础操作 安装app adb install -r xxx.apk / ...

  9. Java、Android中Math详解

    java.math.Math类常用的常量和方法: Math.PI 记录的圆周率 Math.E记录e的常量 Math.abs 求绝对值 Math.sin 正弦函数 Math.asin 反正弦函数 Mat ...

  10. iOS 画图讲解

    5.画图 (1)画线 //绘图代码写在drawRect里,view加载完成,需要显示的时候调用 //1.获取图形上下文 2.创建路径 3.把图形放入上下文 4.渲染上下文 //drawRect的rec ...