【统计数字】数字计数

【问题描述】
	给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。
【输入格式】count.in
	输入文件中仅包含一行两个整数a、b,含义如上所述。
【输出格式】count.out
	输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次。
【输入样例】
	1 99
【输出样例】
	9 20 20 20 20 20 20 20 20 20
【数据规模】
	30%的数据中,a<=b<=106;
	100%的数据中,a<=b<=1012。

朴素的方法就不再多说了,就是枚举区间然后直接累加即可。

当然朴素算法显然要超时。

通过观察可以发现,对于所有i位数,数字1到9出现的次数为i·10^(i-1);数字0出现的次数为i·10^(i-1)(包含前导0)/或9i·10^(i-2)(不包含前导0),那么统计起来就变得非常简单了。

详细统计过程见程序注释。
Accode:

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <string>

typedef long long int64;
int64 x10[20] = {0, 1};
int64 cnt[10];
int64 a, b;
char A[20], B[20];
int la, lb;

inline int64 getint()
{
    int64 res = 0; char tmp;
    while (!isdigit(tmp = getchar()));
    do res = (res << 3) + (res << 1) + tmp - '0';
    while (isdigit(tmp = getchar()));
    return res;
}

int main()
{
    freopen("count.in", "r", stdin);
    freopen("count.out", "w", stdout);
    a = getint(); ++(b = getint());
    for (int i = 1; x10[i] <= b; ++i)
        x10[i + 1] = (x10[i] << 3) + (x10[i] << 1);
    sprintf(A, "%I64d", a); sprintf(B, "%I64d", b);
	//将a、b转换为字符串。
    strrev(A); strrev(B);
	//倒转字符串A、B。
    --(la = strlen(A)); --(lb = strlen(B));
	//得到两字符串的长度。
    for (int i = 1; i < lb + 1; ++i)
        cnt[0] += x10[i - 1] * (i - 1) * 9;
	//统计低位数中出现的0。
    for (int j = 1; j < 10; ++j)
        cnt[j] += x10[lb] * lb;
	//统计低位数中出现的1到9。
    for (int i = lb; i > -1; --i)
    {
        for (int j = i == lb;
	//当为最高位的时候从1开始枚举,否则从0开始枚举。
             j < B[i] - '0'; ++j)
        {
            cnt[j] += x10[i + 1];
	//最后(i-1)位以j开头的数有10^i个。
            for (int k = 0; k < 10; ++k)
                cnt[k] += x10[i] * i;
	//低位数字自由取数,直接累加。
        }
        cnt[B[i] - '0'] += b % x10[i + 1];
	//高位数字还可以取这么多个。
    }

    for (int i = 1; i < la + 1; ++i)
        cnt[0] -= x10[i - 1] * (i - 1) * 9;
    for (int j = 1; j < 10; ++j)
        cnt[j] -= x10[la] * la;
    for (int i = la; i > -1; --i)
    {
        for (int j = i == la;
             j < A[i] - '0'; ++j)
        {
            cnt[j] -= x10[i + 1];
            for (int k = 0; k < 10; ++k)
                cnt[k] -= x10[i] * i;
        }
        cnt[A[i] - '0'] -= a % x10[i + 1];
    }
    for (int j = 0; j < 10; ++j)
        printf("%I64d ", cnt[j]);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值