讨论google面试题 - 在从1到n的正数中1出现的次数

ljsspace 2011-05-23 12:00:11
30.在从1到n的正数中1出现的次数
题目:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。

例如输入12,从1到12这些整数中包含1 的数字有1,10,11和12,1一共出现了5次。
分析:这是一道广为流传的google面试题。


我的一个分治解法:http://blog.csdn.net/ljsspace/archive/2011/05/22/6437981.aspx
请多指教。有更好的解法,也请积极讨论。谢谢!
...全文
7118 95 打赏 收藏 转发到动态 举报
写回复
用AI写文章
95 条回复
切换为时间正序
请发表友善的回复…
发表回复
wltiange 2011-06-02
  • 打赏
  • 举报
回复
将1到n的你个正数拼接成字符串,取字符串的长度然后遍历字符“1”出现的次数
linghuchong001 2011-05-31
  • 打赏
  • 举报
回复
进来看高手解答
qq844628618 2011-05-31
  • 打赏
  • 举报
回复
映射可以解决的
kftbg 2011-05-31
  • 打赏
  • 举报
回复
good
qlz37238 2011-05-30
  • 打赏
  • 举报
回复
#include <stdio.h>
int main(void)
{
int i;
int num;
num=0;
for(i=0;i<=100;i++){
int n=i;
do{
if(n%10==1)
++num;
}while((n/=10)>0);
}
printf("%d",num);
return 0;
}
linghuchong001 2011-05-30
  • 打赏
  • 举报
回复
看高手讨论
zhang3317 2011-05-30
  • 打赏
  • 举报
回复
liujunhg 2011-05-28
  • 打赏
  • 举报
回复

我写的在值范围内,都是秒极时间


public class Count1 {
public long count(long n){
long num=0;
num=(int)Math.ceil(n/10)+1; //获取从1到n之间出现在个位上的1的个数
//计算从十位后每一位上1的个数
for(double i=2d;i<=Long.toString(n).length();i++){
num=num+countWei(n,i) ;
}
return num;
}
/**
* 求1到n之间某位上1的个数
* 如:十位上1的个数就等于n/100取整后乘以10 + ((余数部分-10)大于10那么返回10否则就返回(余数部分-10))
* 百位上1的个数就等于n/1000取整后乘以100 + ((余数部分-100)大于100那么返回100否则就返回(余数部分-100))
* ……
* 以此类推
* X位上1的个数就等于n/X*10取整后乘以X + ((余数部分-X)大于X那么返回X否则就返回(余数部分-X+1))
*
* @param n 最大数(同上)
* @param d 要计算1的个数的某位(如:(10)十位d=2 ,(100)百位d=3……x位d等于x开10次方加1)
* @return
*/
public long countWei(long n,double d){
long num=0;

long weishu= Double.valueOf(Math.pow(10d, d)).longValue();//
num=num+(n/weishu)*(weishu/10);
if((n%weishu-weishu/10)>=(weishu/10)){
num=num+weishu/10;
}else{
num=num+(n%weishu-weishu/10)+1;
};

return num;
}
public static void main(String args[]){
Count1 c=new Count1();
long l=92233720368547l;
System.out.println(c.count(l));
}
}



shiter 2011-05-28
  • 打赏
  • 举报
回复
可以查表的,构造一张足够大的表
myuan605 2011-05-28
  • 打赏
  • 举报
回复
兄弟们有那么复杂吗?
直接把n转换成字符串,遍历字符串的每一个字符,是1的记录一下。
然后从1算到n不就搞定了。
n = f(1) + f(2)+ ... f(n)
intel286 2011-05-28
  • 打赏
  • 举报
回复
喜欢84楼的
strangefx 2011-05-27
  • 打赏
  • 举报
回复

#include<stdio.h>
int static count;
void Sum(int n)
{
while(n!=0)
{
int temp=n%10;
if(temp==1)
count++;
n=n/10;
}
}
void main()
{
for(int i=0;i<=12;i++)
{
Sum(i);
}

printf("%d\n",count);

}
Rico_Liu 2011-05-27
  • 打赏
  • 举报
回复
编程之美中的一题
yinlu78 2011-05-27
  • 打赏
  • 举报
回复
这个问题确实有数学公式可以直接求出,等我有空了把思路大家说一下
lifelongemail 2011-05-27
  • 打赏
  • 举报
回复
哇咔咔~~
wangwindows 2011-05-27
  • 打赏
  • 举报
回复
众多版本.......
jiangchaomr 2011-05-27
  • 打赏
  • 举报
回复
// 不知可不可以

long lifang( long );
long counter( int );
int result( long );
int wei( int );

int main()
{
long n;
cout << "Please input a number: " << endl;
cin >> n;

cout << result( n ) << endl;

return 0;
}

// 求10的num次方
long lifang( long num )
{
long res = 1;
for( long i = 0; i < num; i++ )
{
res *= 10;
}
return res;
}

// 数数
long counter( int number )
{
long count = 1;
for( int i = 1; i < number - 1; i++ )
{
count += count * 9 + lifang( i );
}
return count;
}


// 测试n的位数
int wei( int n )
{
int number = 1;
while( n / 10 != 0 )
{
n /= 10;
number++;
}
return number;
}

int result( long n )
{
int count = 0;
int number = wei( n );

if( number == 1 )
{
if( n == 0 )
{
return count;
}
else
{
return ++count;
}
}
else
{
long lif_num = lifang( number - 1 );
int te = n / lif_num;
//cout << "lif_num: " << lif_num << "te: " << te << endl;
if( te ==1 )
{
count += n - lif_num + 1 + result( n - lif_num ) + counter( number );
}
else
{
//cout << "counter( number ): " << counter( number ) << endl;
count += lif_num + counter( number ) * te + result( n - te * lif_num );
}
}
}
脱管态的狗 2011-05-27
  • 打赏
  • 举报
回复
发一下注释版

/********************************************************************

purpose:

在从1到n的正数中1出现的次数
题目:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。

例如输入12,从1到12这些整数中包含1 的数字有1,10,11和12,1一共出现了5次。

求满足f(i)=i(i<=911111111099999009)这样的数总计有多少个
*********************************************************************/
#include <iostream>
#include <string>
#include <time.h>

using namespace std;


int len;
int *perDigit; // 每位上的数字
long long* fullOne; // 位数是n位的数,含1的总个数为fullOne[n]
long long* addTopOne; // 位数为n的数,首位为1时有多少个这样的数addTopOne[n] 即10^(n-1)


int curDigit;
long long cnt = 0;
long long fixBit1 = 0;
int ocuur1Cnt = 0;
long long getOneCnt(long long n)
{
len = 0;
while (n > 0) { // 把数字n分成十进制串对应的数字数组
perDigit[len] = n % 10;
n = n / 10;
len++;
}
cnt = 0;
fixBit1 = 0; // 数字n中1所在的位被小于此位的数字重复的次数
ocuur1Cnt = 0; // 数字n有几位是1
for (int i = len - 1; i >= 0; i--) { // 从十进制数高位到低位
curDigit = perDigit[i];
if (ocuur1Cnt > 0) {
fixBit1 = fixBit1 * 10 + ocuur1Cnt * curDigit;
}
if (curDigit > 0) {
if (curDigit == 1) {
cnt += curDigit * fullOne[i];
ocuur1Cnt++;
} else {
cnt += curDigit * fullOne[i] + addTopOne[i];
}
}
}
return cnt + fixBit1 + ocuur1Cnt;
}

void HowManyOne(long long topNum)
{
clock_t start, finish; // 记录计算开始结束时间
start = clock();

len = 20; // 最长20位十进制数
perDigit = new int[len];
fullOne = new long long[len];
addTopOne = new long long[len];
fullOne[0] = 0;
addTopOne[0] = 1;
cnt = 1;
for (int i = 1; i < len; i++) { // 初始化信息
fullOne[i] = fullOne[i-1]*10 + cnt;
cnt *= 10;
addTopOne[i] = cnt;
}


long long stack[1000]; // 存储数据段, [from, to]及计算方向,每次分别存入from,to,dir
long long lRel[1000]; // 符合f(i)==i表达式的i的数组
int pStack = 0, pRel = 0; // stack与lRel当前长度或下一次存储位置或栈顶
long long from, to, dir; // 当前要验证的一段数据的始终与验证方向,验证方向为0x01(向上) 0x10(向下) 0x11(向上向下均可以)
long long fn, dist; // fn当前一个数字n对应的1到n所有数字的十进制中的1的总个数;dist临时变量(from与to的差)


stack[0] = 1;
stack[1] = topNum;
stack[2] = 3; // 0x11
pStack = 3;
int maxP = 0;
while (pStack > 0) { // 从stack中取出一段数据,验证其中的i是否满足f(i)==i
dir = stack[--pStack];
to = stack[--pStack];
from = stack[--pStack];

if ((dir & 0x01) != 0) { // UP 从from开始向to的方向计算 f(i)==i
while (from <= to) {
fn = getOneCnt(from);
if (fn > from) {
from = fn;
} else if (fn < from) {
from++;
break;
} else {
lRel[pRel++] = fn;
from++;
}
}
}
if ((dir & 0x10) != 0) { // down 从to开始向from的方向计算 f(i)==i
while(from <= to) {
fn = getOneCnt(to);
if (fn < to) {
to = fn;
} else if (fn > to) {
to--;
break;
} else {
lRel[pRel++] = fn;
to--;
}
}
}
if (to-from<2) { // 这一段己经很小,直接计算完
for (;from<=to;from++) {
if (from == getOneCnt(from)) {
lRel[pRel++] = fn;
}
}
} else { // 当前段向上向下己计算完,二分后入栈,再计算
dist = (to - from) >> 1;
dist = from + dist;
stack[pStack++] = from;
stack[pStack++] = dist;
stack[pStack++] = 0x10;
stack[pStack++] = dist+1;
stack[pStack++] = to;
stack[pStack++] = 0x01;
}

}

finish = clock();
int i = pRel;
while(i > 0) // 输出符合f(i)==i的所有i
cout<<lRel[--i] <<endl;
cout<<"time: " << ((double)(finish-start))<<"ms" << endl;
cout<<"total: " <<pRel<<endl;
}

void Test_HowManyOne()
{
HowManyOne(911111111099999009LL);
}
yyfhz 2011-05-26
  • 打赏
  • 举报
回复
将n位十进制数字排列成如下长度为n的数组,a[1]表示个位,这里要求首位A[n]>=1。
A[n]=(a[n],a[n-1],...,a[2],a[1]),其对应的值表示为A(n)
A[n-1]= (a[n-1],...,a[2],a[1]),其对应的值表示为A(n-1)
...
特别的,令
M[n]=(9[n],9[n-1],...,9[2],9[1]),其对应的值表示为M(n)

令F[A[n]]={从A[n]到0之间总共出现的1的个数}

则有
1. 当a[n]>1时,有
F[A[n]]=(a[n]-2)*F[M[n-1]] -->a[n]从2开始变到a[n]-1为止
+(M(n-1)+1)+F[M[n-1]] -->a[n]=1时的情形
+F[A[n-1]] -->a[n]取为a[n]时的情形。
+F[M[n-1]] -->A长度<n的情形
=a[n]*F[M[n-1]]+F[A[n-1]]+M(n-1)+1
=a[n]*F[M[n-1]]+F[A[n-1]]+10^(n-1)
2. 当a[n]=1时,有
F[A[n]]=F[M[n-1]] -->A长度<n的情形
+(M(n-1)+1)+F[A[n-1]] -->a[n]=1的情形
=F[M[n-1]]+F[A[n-1]]+10^(n-1)
=1*F[M[n-1]]+F[A[n-1]]+10^(n-1)
=a[n]*F[M[n-1]]+F[A[n-1]]+10^(n-1)
可见不论a[n]是否为1,均有
F[A[n]]=a[n]*F[M[n-1]]+F[A[n-1]]+10^(n-1)。 ...(1)
由于从M[n]到0的每一位均有0.1的比率出现1,因此总共出现1的数量为0.1*10^n=10^(n-1),于是又
F[M[n]]=10^(n-1) ...(2)
将(2)代入(1),有
F[A[n]]=a[n]*10^(n-2)+10^(n-1)+F[A[n-1]] ...(3)
利用(3)式以及F[A[1]]=1这个初始条件,直接1个循环,可以在Log(10,s)的时间范围内求得F[s]。
再往下推,有
F[A[n]]=a[n]*10^(n-2)+10^(n-1)+F[A[n-1]]
=a[n]*10^(n-2)+10^(n-1)+a[n-1]*10^(n-3)+10^(n-2)+F[A[n-2]]
=a[n]*10^(n-2)+10^(n-1)+a[n-1]*10^(n-3)+10^(n-2)+a[n-2]*10^(n-4)+10^(n-3)+F[A[n-3]]
=...
=a[n]*10^(n-2) +10^(n-1)
+a[n-1]*10^(n-3)+10^(n-2)
+a[n-2]*10^(n-4)+10^(n-3)
+...
+a[4]*10^(2) +10^(3)
+a[3]*10^(1) +10^(2)
+a[2]*10^(0) +10^(1) +F[A[1]]
=int(a(n)/10) + 10*(1-10^(n-1))/(1-10) + F[A[1]]
=int(a(n)/10) + 10*(10^(n-1)-1)/9+1 ...(4)
(4)式表明,对于任意一个n位正整数x,从0~x之间的1的出现次数(其实也就是从1到x之间的1的出现次数)
F(x)=int(x/10) + 10*(10^(n-1)-1)/9 + 1 这个就是通项公式可以之间拿来求。其中的int(xxx)表示对实数xxx向下取整。

gw6328 2011-05-26
  • 打赏
  • 举报
回复
貌似有难度
加载更多回复(72)

33,009

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧