题意
给定([a,b]),求区间中所有数字中0-9出现的次数。
例如,a=1024,b=1032,则 a 和 b 之间共有9个数如下:
1024 1025 1026 1027 1028 1029 1030 1031 1032
其中‘0’出现10次,‘1’出现10次,‘2’出现7次,‘3’出现3次等等…
思路
理解了数位DP的话,这就是一道简单题了,这里主要是为处理前导0做个笔记,方便以后查阅。
在DFS中是用lead标记是否有前导0限制,lead==1表示有前导0限制。
前导0限制(表示最高位不能取0,例如0123,应该表示成123)向下一层传递的条件是lead==1&&i==0
也就是说当前位有前导0限制,并且当前位取0的时,下一层有前导0限制。
需要记录的是,前(i)位满足条件的数的个数(f[i].cnt),和前(i)位每个数字出现的次数。
递推公式:
第(i)位取(j)时
f[i].cnt+=f[i-1].cnt;
f[i].a[j]+=f[i-1].cnt;//第i位取j时,前i-1为满足条件的数个数就是第i位j出现的次数
for(int j=0;j<=9;j++)f[i].a[j]+=f[i-1].a[j];
代码
#include <bits/stdc++.h>
using namespace std;
struct node{
int cnt;
int a[10];
node(){
cnt=0;
memset(a,0,sizeof a);
}
}f[12];
int a[12];
inline node dfs(int pos,bool limit,bool lead){
if(pos==-1){
node tmp;
tmp.cnt=1;
return tmp;
}
if(!limit&&!lead&&f[pos].cnt!=-1)return f[pos];
int up=limit?a[pos]:9;
node res;
for(int i=0;i<=up;i++){
//if(lead&&i==0)continue;
node tmp=dfs(pos-1,limit&&i==a[pos],lead&&i==0);
res.cnt+=tmp.cnt;
if(!(lead&&i==0)) res.a[i]+=tmp.cnt;
for(int i=0;i<=9;i++)res.a[i]+=tmp.a[i];
}
return (limit||lead)?res:f[pos]=res;
}
inline node sol(int n){
if(!n){
node tmp;
return tmp;
}
memset(f,-1,sizeof f);
int pos=0;
while (n)a[pos++]=n%10,n/=10;
return dfs(pos-1, true,true);
}
int main(){
int l,r;
while (cin>>l>>r,l||r){
if(r<l)swap(l,r);
node a=sol(r);
node b=sol(l-1);
for(int i=0;i<=9;i++){
if(i)cout<<" ";
cout<<a.a[i]-b.a[i];
}
cout<<endl;
}
return 0;
}