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]个,定义如下数组:
要是把这个数组的值全部求到了,那么就好办了。下面找这个数组的规律。
先看看W[3]与W[2]的关系,如下图:
View Code
多多注意从第61行开始的判断:
(1)如果这个数字是大于1的,那么就类似于求W数组的那种方式;
(2)这个数字是0,那么直接跳过,没有贡献;
(3)这个数字是1的话,注意,如果这个1已经是最后一位了,那么直接加一个1就好了。但是如果不是的话,假设这是数字是123了,那么想要去掉这个1直接处理后面的零头23时,[1, 99]可以用W[2]获得,但是这个一对后面的24个数字(00到23)都有影响,所以要加上这么多个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 ; //数组含义:下标 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。
可以运行试试。
相关文章推荐
- 讨论google面试题 - 在从1到n的正数中1出现的次数
- 【google面试题】求1到n的正数中1出现的次数的两种思路及其复杂度分析
- 计算字符串中每种字符出现的次数(面试题)
- 黑马程序员------面试题 计算字符出现次数
- 程序员面试题精选100题(25)-在从1到n的正数中1出现的次数
- 易车网的一道面试题:JS计算字符串中出现次数最多的字符和出现的次数
- 面试题32:在从1到n的正数中1出现的次数
- 面试题: 请用代码来计算字符串"I'm go to swimming"中每一个字母出现的次数,并打印出来
- Google面试算法题:《从1到n的正数中1出现的次数》的求解
- 剑指offer 面试题32 计算1~n中1出现的次数
- 程序员面试题精选100题(25)-在从1到n的正数中1出现的次数[算法]
- 程序员面试题精选100题(25)-在从1到n的正数中1出现的次数[算法]
- google笔试题:计算1-N(N=1234567890)中1出现的次数。
- 程序员面试题精选100题(25)-在从1到n的正数中1出现的次数
- 程序员面试题精选100题(25)-在从1到n的正数中1出现的次数
- 面试题 在从1到n的正数中1出现的次数 的一个解题思路
- Java常用面试题17 计算文件中指定字符出现的次数
- 用Regex类计算一个字符串出现次数是最好方法【转载】
- 面试题精选(67):1.。。。n中'1'出现的次数
- ORACLE计算字符在字符串中出现的次数