题意:
给定两个正整数 a 和 b,求在 [a,b] 中的所有整数中,每个数码(digit)各出现了多少次。
数据范围:1 ≤ a ≤ b ≤
题解:
这道题的范围到,O(n)的做法铁超时,这里标答是数位DP或记搜
1.预处理
打表看一下,0-9出现的次数有规律,对于0-9 ,0 - 99,0 - 999这样的数据1-9个数是相等的(0特殊)
我们令f[i]为最高位为i位时,1-9出现的次数,那么,如果不考虑前导0,0与其他数字的次数是相等的,这里观察发现,0的出现次数=(这里去前导0我放到了递归里)
void init(){//预处理,把f[i][k]全求出来
for(int i=1;i<=13;i++)//i到第13位
for(int k=0;k<=9;k++)
f[i][k]= i * pow(10,i-1);
}
2.
之后我们按位从高到低拆分,这里我们可以发现
对于3939,找3的次数,我们可以将其分成 三部分:最高位*f[i-1] + find(去掉最高位后的数(939)+ 最高位的出现的次数(939+1=940)
特别的,这里需要提出最高位maxn,与要找的数k进行讨论,maxn=k,则 ans[k] += 。
maxn == k(如样例),ans[k] += 下一位为最高位的数+1 (939 + 1),maxn < k忽略。
最后答案等于 sum[0,B] - sum[0,A-1](前缀和)
3.完整代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll f[20][10],a,b,ansa[10],ansb[10],cnta,cntb;//f[i][k]表示第i位数字k有多少
void init(){//预处理,把f[i][k]全求出来
for(int i=1;i<=13;i++)//i到第13位
for(int k=0;k<=9;k++)
f[i][k]= i * pow(10,i-1);
}
void finda(ll num,ll cnt,ll k){//数字num,cnt当前的位数
if(cnt==0) return ;
ll maxn = num / pow(10,cnt-1);//maxn是最高位的数
ansa[k] += f[cnt-1][k] * maxn;
if(maxn > k) ansa[k] += (ll)pow(10,cnt-1) ;
else if(maxn == k)ansa[k] += num % (ll)pow(10,cnt-1) + 1;
if(k == 0) ansa[k] -= pow(10 , cnt-1);
finda(num % (ll)pow(10,cnt-1) , cnt-1 , k);
}
void findb(ll num,ll cnt,ll k){//数字num,cnt当前的位数
if(cnt==0) return ;
ll maxn = num / pow(10,cnt-1);//maxn是最高位的数
ansb[k] += f[cnt-1][k] * maxn;
if(maxn > k) ansb[k] += (ll)pow(10,cnt-1) ;
else if(maxn == k)ansb[k] += num % (ll)pow(10,cnt-1) + 1;
if(k == 0) ansb[k] -= pow(10 , cnt-1);
findb(num % (ll)pow(10,cnt-1) , cnt-1 , k);
}
void getcnt(){
ll aa = a, bb = b;
while(aa){
cnta++;
aa/=10;
}
while(bb){
cntb++;
bb/=10;
}
}
int main(){
cin >> a >> b;
a--;
init();
getcnt();
for(int k = 0;k <= 9;k++)
finda(a,cnta,k);
for(int k = 0;k <= 9;k++)
findb(b,cntb,k);
for(int k = 0;k <= 9;k++)
cout<<ansb[k] - ansa[k]<<" ";
return 0;
}