zoukankan      html  css  js  c++  java
  • Google面试题:计算从1到n的正数中1出现的次数

    题目:

      输入一个整数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[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 了,这种情况要注意处理好,下面直接上代码,注释很详细啦:

      1 #include <iostream>
      2 #include <sstream>
      3 #include <string>
      4 #include <cmath>
      5 #include <cstdlib>
      6 #include <fstream>
      7 #include <ctime>
      8 
      9 using std::cout;
     10 using std::endl;
     11 using std::cin;
     12 using std::string;
     13 
     14 //最原始的方法,即从1到n,看每个数字中的“1”的个数,然后加到一起
     15 int getCntSimple(int n)
     16 {
     17     int cnt = 0;
     18     for (int i = 1; i <= n; i++)
     19     {
     20         //数字到字符串的转换
     21         std::stringstream ss;
     22         ss << i;
     23         string temp = ss.str();
     24         
     25         int thisCnt = 0;
     26         for (int j = 0; j < temp.size(); j++)
     27         {
     28             if ('1' == temp[j])
     29                 thisCnt++;
     30         }
     31 
     32         cnt += thisCnt;
     33     }
     34     return cnt;
     35 }
     36 
     37 //较好的方法
     38 enum{N = 5};
     39 int W[N];        //数组含义:下标 i 代表整数位数是 i 的共有包含多少个 1,如 i 是 2 代表[1, 99]区间全部整数共包含多少个 1
     40 
     41 //初始化W数组
     42 void initWArr(void)
     43 {
     44     W[0] = 0;
     45     for (int i = 0; i < N - 1; i++)
     46     {
     47         W[i + 1] = W[i] * 10 + std::pow(10, i);
     48     }
     49 }
     50 
     51 int getCntBetter(int n)
     52 {
     53     //数字转成字符串
     54     std::stringstream ss;
     55     ss << n;
     56     string temp = ss.str();
     57 
     58     int len = temp.size(), res = 0;
     59 
     60     //其实这里的每次循环处理的都是最大的一位,以后的都相当于零头子
     61     for (int i = 0; i < len; i++)
     62     {
     63         int num = (temp[i] - '0');        //由字符转为数字
     64 
     65         if (num > 1)        //注意当前这一位是不是比1大
     66         {
     67             res += num * W[len - i - 1] + std::pow(10, len - i - 1);        //比1大就直接加上10^(len - i - 1)个,完整的那么多次方个,完全贡献
     68         }
     69         else if (0 == num)        //如果这个数是0
     70         {
     71             continue;        //直接进行下一次循环
     72         }
     73         else    //这数字是1,下个循环要去掉这个位,得先把这个1的贡献算上
     74         {
     75             if (i < len - 1)            //不是最后一位的1
     76             {
     77                 //获取这个1后面的数字
     78                 string strVal = temp.substr(i + 1);
     79                 std::stringstream ss(strVal);
     80                 int val;
     81                 ss >> val;
     82 
     83                 res += W[len - i - 1] + (val + 1);
     84             }
     85             else     //最后一位了,且是1
     86             {
     87                 res += 1;
     88             }
     89         }
     90     }
     91 
     92     return res;
     93 }
     94 
     95 int main(void)
     96 {
     97     initWArr();
     98     std::ofstream fout("src.txt");
     99 
    100     /*
    101     for (int i = 0; i < 2000; i++)
    102     {
    103         int n =std::rand() % 1000;
    104         int a = getCntSimple(n);
    105         int b = getCntBetter(n);
    106         fout << n << "   :" << a << "  " << b << endl;
    107         cout << i << ((a == b) ? " 是" : " 否") << endl;
    108     }*/
    109 
    110     clock_t aTimeStart = std::clock();
    111     for (int i = 0; i < 2000; i++)
    112     {
    113         int a = getCntSimple(i);
    114     }
    115     clock_t aTimeEnd = std::clock();
    116     cout << "runing time a: " << static_cast<double>(aTimeEnd - aTimeStart) / CLOCKS_PER_SEC * 1000 << "ms" << endl;
    117     
    118     clock_t bTimeStart = std::clock();
    119     for (int i = 0; i < 2000; i++)
    120     {
    121         int a = getCntBetter(i);
    122     }
    123     clock_t bTimeEnd = std::clock();
    124     cout << "runing time b: " << static_cast<double>(bTimeEnd - bTimeStart) / CLOCKS_PER_SEC * 1000 << "ms" << endl;
    125 
    126     cin.get();
    127 }
    View Code

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

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

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

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

    可以运行试试。

  • 相关阅读:
    sklearn之集成算法模型
    ScrollView嵌套ListView,GridView数据加载不全问题的解决
    ProgressBar---进度条
    Android中Button的五种监听事件
    ListView加载性能优化---ViewHolder---分页
    Menu与ActionBar的爱恨情仇
    toast组件小结
    数据挖掘导论---揭开数据挖掘神秘面纱-1
    腾讯地图坐标转百度地图坐标
    微信录音接口的调用以及amr文件转码MP3文件的实现
  • 原文地址:https://www.cnblogs.com/jiayith/p/3925393.html
Copyright © 2011-2022 走看看