具体考虑每一位中出现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;
}