数位dp

[Algo] 数位dp

1. 最大为N的数字组合

// 1. 最大为N的数字组合
// https://leetcode.cn/problems/numbers-at-most-n-given-digit-set/
int func1(vector<int>& digit, int n, int len, int offset, int free, int fix) {
    // free = 1: 当前位置可以任意选取数字
    // fix  = 0: 之前从未选取过数字(当前位置可以不选取数字)
    if (len == 0) return fix;
    int ans = 0;
    if (fix == 0) ans += func1(digit, n, len - 1, offset / 10, 1, 0);
    if (free == 1) ans += digit.size() * func1(digit, n, len - 1, offset / 10, 1, 1);
    else {
        int cur = (n / offset) % 10;
        for (int number : digit) {
            if (number < cur) ans += func1(digit, n, len - 1, offset / 10, 1, 1);
            else if (number == cur) ans += func1(digit, n, len - 1, offset / 10, 0, 1);
            else break;
        }
    }
    return ans;
}
int func2(vector<int>& digit, int n, int len, int offset, vector<int>& help) {
    if (len == 0) return 1;
    int cur = (n / offset) % 10, ans = 0;
    for (int number : digit) {
        if (number < cur) ans += help[len - 1];
        else if (number == cur) ans += func2(digit, n, len - 1, offset / 100, help);
        else break;
    }
    return ans;
}
int atMostNGivenDigitSet(vector<string>& digits, int n) {
    if (n == 0) return 0;
    int num = digits.size();
    vector<int> digit(num);
    for (int i = 0; i < num; i++) digit[i] = digits[i][0] - '0';
    int len = 1, offset = 1, tmp = n / 10;
    while (tmp > 0) {
        len++;
        offset *= 10;
        tmp /= 10;
    }
    // return func1(digit, n, len, offset, 0, 0);
    vector<int> help(len);
    help[0] = 1;
    int sum = 0;
    for (int i = 1; i < len; i++) {
        help[i] = help[i - 1] * num;
        sum += help[i];
    }
    return sum + func2(digit, n, len, offset, help);
}

2. 统计整数数目

// 2. 统计整数数目
// https://leetcode.cn/problems/count-of-integers/
int func3(vector<int>& num, int min_sum, int max_sum, int index, int sum, int free, vector<vector<vector<int>>>& dp) {
    int n = num.size();
    // <--------------------剪枝-------------------->
    if (sum > max_sum) return 0;
    if (sum + 9 * (n - index) < min_sum) return 0;
    // <-------------------------------------------->
    if (index == n) return 1;
    if (dp[index][sum][free] != -1) return dp[index][sum][free];
    int ans = 0;
    if (free == 1) {
        for (int i = 0; i <= 9; i++) ans = (ans + func3(num, min_sum, max_sum, index + 1, sum + i, 1, dp)) % MOD;
    } else {
        for (int i = 0; i < num[index]; i++) ans = (ans + func3(num, min_sum, max_sum, index + 1, sum + i, 1, dp)) % MOD;
        ans = (ans + func3(num, min_sum, max_sum, index + 1, sum + num[index], 0, dp)) % MOD;
    }
    dp[index][sum][free] = ans;
    return ans;
}
int check(vector<int>& num, int min_sum, int max_sum) {
    int n = num.size(), sum = 0;
    for (int i = 0; i < n; i++) sum += num[i];
    if (sum >= min_sum && sum <= max_sum) return 1;
    else return 0;
}
int count(string num1, string num2, int min_sum, int max_sum) {
    int n = num1.size(), m = num2.size();
    vector<int> v1(n), v2(m);
    for (int i = 0; i < n; i++) v1[i] = num1[i] - '0';
    for (int i = 0; i < m; i++) v2[i] = num2[i] - '0';
    vector<vector<vector<int>>> dp1(n, vector<vector<int>>(max_sum + 1, vector<int>(2, -1)));
    vector<vector<vector<int>>> dp2(m, vector<vector<int>>(max_sum + 1, vector<int>(2, -1)));
    int cnt1 = func3(v1, min_sum, max_sum, 0, 0, 0, dp1);
    int cnt2 = func3(v2, min_sum, max_sum, 0, 0, 0, dp2);
    return ((cnt2 - cnt1 + MOD) % MOD + check(v1, min_sum, max_sum)) % MOD;
}

3. 统计特殊整数

// 3. 统计特殊整数
// https://leetcode.cn/problems/count-special-integers/
int func4(int n, int len, int offset, vector<int>& help, int status) {
    if (len == 0) return 1;
    int ans = 0, cur = n / offset % 10;
    for (int i = 0; i < cur; i++) {
        if (i == 0 && n / offset < 10) continue;
        if ((status & (1 << i)) == 0) ans += help[len - 1];
    }
    if ((status & (1 << cur)) == 0) ans += func4(n, len - 1, offset / 10, help, status | (1 << cur));
    return ans;
}
int countSpecialNumbers(int n) {
    int len = 1, offset = 1, tmp = n / 10;
    while (tmp > 0) {
        len++;
        offset *= 10;
        tmp /= 10;
    }
    if (len == 1) return n;
    int ans = 9;
    for (int i = 2, j = 9, k = 9; i < len; i++, k--) {
        j *= k;
        ans += j;
    }
    vector<int> help(len);
    help[0] = 1;
    for (int i = 1; i < len; i++) help[i] = help[i - 1] * (10 + i - len); 
    return ans + func4(n, len, offset, help, 0);
}

4. 不含连续1的非负数

// 4. 不含连续1的非负数
// https://leetcode.cn/problems/non-negative-integers-without-consecutive-ones/description/
int func5(int n, vector<int> &cnt, int i, int p) {
    if (i == -1) return 1;
    int cur = (n >> i) & 1, ans = 0;
    if (cur == 1) {
        ans = cnt[i];
        if (p == 1) return ans;
    }
    return ans + func5(n, cnt, i - 1, cur);
}
int findIntegers(int n) {
    vector<int> cnt(32);
    cnt[0] = 1;
    cnt[1] = 2;
    for (int i = 2; i < 32; i++) {
        cnt[i] = cnt[i - 1] + cnt[i - 2];
    }
    return func5(n, cnt, 30, 0);
}

5. 数字1的个数

// 5. 数字1的个数
// https://leetcode.cn/problems/number-of-digit-one/
int countDigitOne(int n) {
    int ans = 0;
    for (long tmp = n, left, right = 1; tmp != 0; tmp /= 10, right *= 10) {
        int cur = tmp % 10;
        left = tmp / 10;
        ans += left * right;
        if (cur > 1) {
            ans += right;
        } else if (cur == 1) {
            ans += n - right * tmp + 1;
        }
    }
    return ans;
}
posted @ 2025-06-05 14:00  yaoguyuan  阅读(11)  评论(0)    收藏  举报