数字x在1~N中出现的次数

具体考虑每一位中出现x的次数,算出每个前缀能出现的x的次数。

ex:369中1出现的次数
个位:将个位置为1,个位往上有37(0~36),所以有37*1次。
但是仔细一想,是不是有点bug?有没有可能在前缀为36时取不到1?有可能的,因为如果此时个位小于1的话(如360,则没有361这种情况)。
那如果等于1呢?
所以需要特判此位是否大于等于1。
十位:将十位置为1,十位以上有4(0~3),所以有4*10次。
但是又仔细一想,是不是还有bug?万一此位等于1,则此时取不完该前缀的全部情况(如316,则前缀为31时取不到317~319这些情况)。
以上:以此类推……

对于前缀0–r:
首先考虑0–r-1时,该位都能取完全部base;
其次考虑r,该位能不能取完全部base取决于该位是否大于x;
小于等于x:
小于x则base都不能取;
等于x则只能取完低位;

ex:
215-2:(22*1)+(2*10)+(0*100+15+1)== 58
个位:00–21都能取到2共22
十位:0–1都能取到2(20–29共10个数),2取不到2(取不到220)共20
百位:0能取到2,只能取到0200–0215(低位15+0=16)共16

// 1~n中x出现的次数  O(logn)
ll get_x(ll n, int x) {
	ll res = 0, base = 1, pre = 0;
	while (n) {
		ll r = n / 10;      // 此时的前缀
		int num = n % 10;   // 该位数值
		res += base * r;    // 先考虑前缀0~r-1
		if (num > x)        // 如果大于x
			res += base;    // 则前缀r也能全部取完base
		if (num == x)       // 刚好等于x
			res += (pre+1); // 只能取完该位以前的数值
		pre += num * base;  // 记录该位以前的数值
		base *= 10;         // base记录位数
		n /= 10;
	}
	return res;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值