计数问题(思维题目)

C++

计数问题

/*
问题描述:
    给定两个整数 a 和 b,求 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 次等等…

    0 < a, b < 100000000

解题思路:
    本题思路主要在于讨论,并且一步步将大问题分解为小问题。
    Q1 --> 求解 [a, b] 区间中 0, 1, 2, ..., 9 数字的出现个数
    Q2 --> 求解 [0, x] 区间中 0, 1, 2, ..., 9 数字的出现个数,因为 [0, b] - [0, a - 1] 即为上一步结果
    Q3 --> 求解 [0, x] 区间中 数字 t 的出现次数,将 t 取值 0, ..., 9 即为上述结果。
    Q4 --> 求解 [0, x] 区间中,数字 t 在第 i 位上出现的次数,取遍所有的位置,即为上一步的结果。
    现在,我们将问题分解的差不多了,求解 Q4
        假设数字为 abcdef,第一位数字为 a, 第二位数字为 b,第 i 位数字为 d, 最后一位数字(第 n 位数字)为 f
      当 d = t:
        当前面取值 abc,第 i 位置取 d,这种情况可以从 (0 .. ef),因为出现次数为 ef + 1
        当 t = 0:
            (1 .. abc) * 10 ^ (n - i) -> abc * 10 ^ (n - i)
        当 t != 0
            (0 .. abc) * 10 ^ (n - i) -> (abc + 1) * 10 ^ (n - i)
        
        结果为 (ef + 1) + (abc * 10 ^ (n - i) if t == 0 else (abc + 1) * 10 ^ (n - i))
      当 d > t:
        当 t == 0:
            (1 ... abc) * 10 ^ (n - i) -> abc * 10 ^ (n - i)
        当 t != 0:
            (0 ... abc) * 10 ^ (n - i) -> (abc + 1) * 10 ^ (n - i)
      当 d < t:
        当 t == 0:
            (1 ... abc-1) * 10 ^ (n - i) -> (abc - 1) * 10 ^ (n - i)
        当 t != 0:
            (0 ... abc-1) * 10 ^ (n - i) -> abc * 10 ^ (n - i)


*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;

int pow_10[20];

int cal_num(vector<int> &nums, int l, int r) {
    int ret = 0;
    for (int i = l - 1; i < r && i < nums.size(); i ++ ) {
        ret = ret * 10 + nums[i];
    }
    return ret;
}

int get_single_digital_cnt_per_position(vector<int> &nums, int i, int digit) {
    int pre_num = cal_num(nums, 1, i - 1);
    int post_num = cal_num(nums, i + 1, nums.size());

    int ret_cnt = 0;
    if (nums[i - 1] == digit) {
        ret_cnt = post_num + 1;
        if (digit == 0) {
            ret_cnt += (pre_num - 1) * pow_10[nums.size() - i];
        } else {
            ret_cnt += (pre_num) * pow_10[nums.size() - i];
        }
    } else if (nums[i - 1] > digit) {
        if (digit == 0) {
            ret_cnt += (pre_num) * pow_10[nums.size() - i];
        } else {
            ret_cnt += (pre_num + 1) * pow_10[nums.size() - i];
        }
    } else {
        if (digit == 0) {
            ret_cnt += (pre_num - 1) * pow_10[nums.size() - i];
        } else {
            ret_cnt += (pre_num) * pow_10[nums.size() - i];
        }
    }

    return ret_cnt;
}


int get_single_digital_cnt(int x, int digit) {
    vector<int> nums;
    int y = x, len = 0;
    if (y == 0) {
        len = 1;
        nums.push_back(0);
    } else {
        while (y) {
            nums.push_back(y % 10);
            y /= 10;
            len += 1;
        }
        reverse(nums.begin(), nums.end());
    }

    int cnt = 0;
    for (int i = 1; i <= len; i ++ ) {
        cnt += get_single_digital_cnt_per_position(nums, i, digit);
    }
    
    return cnt;
}


vector<int> get_all_digital_cnt(int x) {
    if (x == 0) {
        vector<int> ret = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
        return ret;   
    }
    vector<int> ret;
    for (int i = 0; i <= 9; i ++ ) {
        ret.push_back(get_single_digital_cnt(x, i));
    }
    return ret;
}

int main()
{
    int a, b;
    pow_10[0] = 1;
    for (int i = 1; i <= 9; i ++ ) {
        pow_10[i] = 10 * pow_10[i - 1];
    }

    
    while (scanf("%d%d", &a, &b), a != 0 || b != 0) {
        // 确保 a <= b
        if (a > b) {
            swap(a, b);
        }

        vector<int> res_a = get_all_digital_cnt(a - 1);
        vector<int> res_b = get_all_digital_cnt(b);


        printf("%d", res_b[0] - res_a[0]);
        for (int i = 1; i < res_b.size(); i ++ ) {
            printf(" %d", res_b[i] - res_a[i]);
        }
        printf("\n");
    }
    

    return 0;
}
posted @ 2022-07-09 15:39  lucky_light  阅读(118)  评论(0编辑  收藏  举报