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 = };
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出现的次数的更多相关文章
- 【google面试题】求1到n的正数中1出现的次数的两种思路及其复杂度分析
问题描写叙述: 输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数.比如输入12,从1到12这些整数中包括1 的数字有1.10.11和12.1一共出现了5次. 这是一道广为流传的googl ...
- 【面试题032】从1到n整数中1出现的次数
[面试题032]从1到n整数中1出现的次数 题目: 输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数. 例如输入12,从1到12这些整数中包含1的数字有1,10,11和1 ...
- 面试题32:从1到n整数中1出现的次数
这是一道广为流传的google面试题.用最直观的方法求解并不是很难,但遗憾的是效率不是很高:而要得出一个效率较高的算法,需要比较强的分析能力,并不是件很容易的事情.当然,google的面试题中简单的也 ...
- 【剑指offer】面试题32:从1到n整数中1出现的次数
题目: 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了.A ...
- 面试题四十三:在1~n整数中1出现的次数
方法一:直观来看,遍历1到n,每个数去做%10的循环判断 int Number1_B_1toN( int n){ int sum=0; for(int i=1;i<=n;i++){ int k= ...
- (剑指Offer)面试题32:从1到n整数中1出现的次数
题目: 输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数.例如输入12,从1到12这些整数中包含1的数字有1,10,11和12,一共出现了5次. 思路: 1.累加法 累加1到n中每个整数 ...
- 数组中第K小的数字(Google面试题)
http://ac.jobdu.com/problem.php?pid=1534 题目1534:数组中第K小的数字 时间限制:2 秒 内存限制:128 兆 特殊判题:否 提交:1120 解决:208 ...
- Google 面试题和详解
Google的面试题在刁钻古怪方面相当出名,甚至已经有些被神化的味道.这个话题已经探讨过很多次,而科技博客 BusinessInsider这两天先是贴出15道Google面试题并一一给出了答案,其中不 ...
- Google面试题(选自公众号)
date: 2018-12-14 13:34:56 updated: 2018-12-14 13:34:56 Google面试题(选自公众号) 问题 把你的手机拨号页想象成一个棋盘.棋子走只能走&qu ...
随机推荐
- xcode中没有autoSizing的设置
转自:http://blog.sina.com.cn/s/blog_954bb2f001016oyx.html 学习Xcode的iOS编程时,可能会发现Autosizing Control不见了,其原 ...
- 实现O(1)获取最大最小值的栈----java
原文:http://blog.csdn.net/sheepmu/article/details/38459165 实现O(1)获取最大最小值的栈和队列----java 一.如何实现包含获取最小值函数的 ...
- Amazon 开始接受 Windows 礼品卡预订
在 8 月微软虚拟货币系统 Microsoft Points 已经正式被真实货币替代,但目前,配套真实货币系统将推出的礼品卡还并没有开始销售.Amazon 上的一则预订显示“Windows 礼品卡”( ...
- TCP/IP协议原理与应用笔记24:网际协议(IP)之 IP协议的简介
1. IP协议:(网际协议) 2. IP协议(不可靠无连接数据报协议) (1)连接 vs. 可靠性 连接:面向连接.无连接 可靠:差错控制.流量控制等 面向连接不一定保证可靠,无连接不一定不可靠 在连 ...
- MySQL(23):事务的隔离级别出现问题之 脏读
1. 脏读 所谓的脏读就是指一个事务读取了另一个事务未提取的数据. 试想一下:a账户要给b账户100元购买商品,如果a账户开启一个事务,执行下面的update语句做了如下转账的工作: update a ...
- Android进阶笔记17:3种JSON解析工具(org.json、fastjson、gson)
一. 目前解析json有三种工具:org.json(Java常用的解析),fastjson(阿里巴巴工程师开发的),Gson(Google官网出的),其中解析速度最快的是Gson. 3种json工具下 ...
- 沈逸老师PHP魔鬼特训笔记(1)
此课程个人开发环境可以考虑使用Ubuntu ,推荐sublime和PhpStorm作为开发环境.一.PHP的一大特性是:脚本语言.不要编译,写完就可以运行? 然而并不是....... PHP代码要想运 ...
- 个人总结ANDROID开发事项
theme:none,Holo Dark(全黑),Holo Light(全白),Holo Light width Dark action Bar(全白,活动栏是黑) Create Activity: ...
- Mindjet MindManager 2016/2017 折腾记录
https://community.mindjet.com/mindjet/topics/ensure-2017-64-bit-version-installation Mindmanager sho ...
- jQuery过滤选择器:not()方法使用介绍
在jQuery的早期版本中,:not()筛选器只支持简单的选择器,说明我们传入到:not这个filter中的selector可以任意复杂,比如:not(div a) and :not(div,a) & ...