0-9,0-99,0-999,0-9999 ........
當中每一位數字出現的次數都是一樣的。
f[i] 對於i位數的數字,每個數字出現的次數,f[i] = f[i - 1] * 10 + 10^(i - 1)
但是這裡會有一個問題,就是前置0,因為對於上面的公式會把0001,0002,0003 當中出現的0也計算進0的出現次數中,所以最後我們要扣除這些前導0的出現次數。
假設說我們已經知道不同位數不同數字的出現次數,我們可以把一個4位數看成ABCD, 我們可以輕易算出A000中不同數字的出現次數,也就是f[i - 1] * A, 然後加上A還出現了 BCD + 1次,所以要加上BCD+1,然後我們還要加上BCD中 所有數字的出現次數,然後就是CD,然後就是D,問題的規模也就越變越小了。
答案要求我們求出[ a, b] ,我們可以簡單點先計算a - 1的答案,在計算 1到b的答案,最後相減就是區間a,b的答案了。
#include <iostream>
#include <cstring>
using namespace std;
long long a, b;
long long cnta[25], cntb[25];
long long f[25], ten[25];
void solve(long long x, long long *cnt){
int len = 0;
long long nums[25] = {0};
while(x){
nums[++len] = x%10;
x/=10;
}
//從A到D
for(int i = len; i >= 1; i--){
//A000所有字數出現次數
for(int j = 0; j <= 9; j++){
cnt[j] += nums[i] * f[i - 1];
}
//A000所有字數出現次數,j 一旦大於或等於A就停止
for(int j = 0; j < nums[i]; j++){
cnt[j] += ten[i - 1];
}
long long sum = 0;
//計算BCD
for(int j = i - 1; j >= 1; j--){
sum = sum * 10 + nums[j];
}
cnt[nums[i]] += sum + 1;
cnt[0] -= ten[i - 1];
}
}
int main(){
cin >> a >> b;
memset(f, 0, sizeof(f)); memset(cnta, 0, sizeof(cnta)); memset(cntb,0,sizeof(cntb));
ten[0] = 1;
for(int i = 1; i <= 15; i++){
f[i] = f[i - 1] * 10 + ten[i - 1];
ten[i] = ten[i - 1]*10;
}
solve(a - 1, cnta);
solve(b, cntb);
for(int i = 0; i <= 9; i++)cout << cntb[i] - cnta[i] << " ";
return 0;
}