【LeetCode-Hard-2】【Number of Digit One】【1~N中‘1’出现次数】

第二题,中等难度,看到正确率略小于0.2就做了

233 Number of Digit One 19.5% Medium

Given an integer n, count the total number of digit 1 appearing in all non-negative integers less than or equal to n.

For example:
Given n = 13,
Return 6, because digit 1 occurred in the following numbers: 1, 10, 11, 12, 13.

求1~N中‘1’出现的次数,例如N==13,‘1’出现在1,10,11(2次),12,13中,共6次

==========================================================

首先如果N是int类型的话,取接近最大值是必然会溢出的,所以leetcode的测试集还是手下留情了。

比如取N=20亿(2^31-1略大于21亿),1~10亿共会出现9亿+1个‘1’(0~9 9999 9999没位1/10几率出现‘1’共 【9位*(1/10)*10亿个数】,再加上10亿零头的一个‘1’)

然后10亿~20亿 == (10+0)亿~(10+10亿),每个数的十亿位都有一个‘1’,再加上个位到亿位9亿+1

有28亿个‘1’溢出了(去掉为方便说明的零头1,重复计数了)

System.out.println(nd.getNum(2000000000)-2000000000);     输出结果为8亿整,正确

(剧透思路了。。)

==========================================================

最简单的思路是遍历1~n,累加每个数字中出现的‘1’的次数。

毫无疑问,这样做肯定会超时。但是!这个方法却可以生成N的测试集!但凡是正确的解决方案,都有一定的价值。

在可以自己生成测试集的情况下,答题正确率不到0.2,可见具体过程还是有些小坑,且刷题人都是随便一刷

public int getNum_basic(int n){
		if(n == 0) return 0;
		int count = 0;
		
		for(int i=1;i<=n;i++){
			for(int j=i;j>0;j/=10){
				if(j%10 == 1) count++;
			}
		}
		
		return count;
	}
==========================================================
优化思路,转化为数值计算。

例如 N=333,从百位上看,经历了4个过程:【000~099】【100~199】【200~299】【300~333】

诶!前几个过程是重复的耶!

所以,前三个过程可以直接计算‘1’的次数为 3*20  (个位十位分别出现10次1;同理从千位来看,个位十位百位分别出现10^2次1,如果N=3333,0~2999共出现3*300次‘1’)

(记得加上百位的1,共100个)

剩下的300~333,不是跟0~33出现‘1’的次数一样么。

最终求 0/1~333 的问题转化为求 1~300 + 1~30 + 1~3 的问题。


其中有个特殊情况是某位为1,比如33133

转化为 30000 + 3000 +133 后,133百位的1并没有出现100次,只有0~33共34次。


至于某位为0的情况,跳过此位即可


最终的算法,可以从高位或低位开始扫描都OK,

我选择的是低位开始,因为每次循环取位N%10后N/=10,取数字方便;缺点是要用一个变量来记录已扫描的值record,以便在出现某位为1的情况时得到该位1的次数。

高位开始的话,一开始需要计算总共位数bit(不是2进制bit),且最好直接计算一个tens值记录10^bit,每次循环tens/=10;这样就不用记录已扫描的值。


代码:

public int getNum(int n){
		if(n<1) return 0;
		int count = 0;
		
		int bit = 1;
		int tail = 1;
		int tens = 1;
		int record = 0;
		for(int i=n;i>0;i/=10){
			int baseCount = (bit-1)*(tens/10);	//1234	4-0*0  3-1*1 2-2*10 1-3*100
			bit++;
			tail = i % 10;
			
			if(tail == 1){
				count += record+1 + baseCount;
			}else if(tail > 1){
				count += tens + baseCount*(tail);//1xx && 1/2/3/后的xx中的1
			}
			record += tail * tens;				//1234	4	   34	 234	1234
			tens *= 10;
		}
		
		return count;
	}

运行结果:



第一次用java刷OJ,之前都是用C,即使是主要用其他语言工作的时候(说得好像有用C工作过一样),现在觉得还是慢点的好。

C码结果是这样的:|。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值