【数位DP模板题】

为灵茶山艾府疯狂打call!!!

2376. 统计特殊整数

题目描述

描述:如果一个正整数每一个数位都是 互不相同 的,我们称它是 特殊整数 。

给你一个 正 整数 n ,请你返回区间 [1, n] 之间特殊整数的数目。

示例 1:

输入:n = 20
输出:19
解释:1 到 20 之间所有整数除了 11 以外都是特殊整数。所以总共有 19 个特殊整数。

示例 2:

输入:n = 5
输出:5
解释:1 到 5 所有整数都是特殊整数。

示例 3:

输入:n = 135
输出:110
解释:从 1 到 135 总共有 110 个整数是特殊整数。
不特殊的部分数字为:22 ,114 和 131 。

提示:

1 <= n <= 2 * 109

解题思路

class Solution {
public:
    int countSpecialNumbers(int n) {
        //将n转换为字符串
        string s=to_string(n);
        int m=s.length();
        //记录字符串s每一位的数字是否出现过(0、1、2、3...一共十位)
        int memo[m][1<<10];
        //初始化为-1表示没计算过
        memset(memo,-1,sizeof(memo));
        //匿名函数内可以使用外部变量 返回值int 参数int int bool bool
        //i表示当前位 mask是十位二进制表示当前那些位出现过 
        //is_limit表示当前位是否被限制 如果不是则范围是0~9 如果是则是0~i
        //is_num表示i前面的数位是否填了数字 如果没有则当前位也可以不填 反之则当前位可以填0
        function<int(int,int,bool,bool)> f=[&](int i,int mask,bool is_limit,bool is_num)->int{
            if(i==m)
                return is_num; //is_num为true表示得到了一个合法的数字
            if(!is_limit&&is_num&&memo[i][mask]!=-1)
                return memo[i][mask]; //之前计算过
            int res=0;
            //i前面的数位没有填数字 则跳过当前位 并且没有限制 
            if(!is_num)
                res=f(i+1,mask,false,false);
            //如果被限制则是0~i反之是0~9
            int up=is_limit?s[i]-'0':9;
            //枚举要填入的数字 i前面填了数字则从0开始反之从1开始
            for(int d=1-is_num;d<=up;d++)
            {
                //d不在mask中
                if(((mask>>d)&1)==0)
                    //加入到mask中 当前位到达最大值并且前面有限制则后面有限制
                    res+=f(i+1,mask|(1<<d),is_limit&&d==up,true);
            }
            if(!is_limit&&is_num)
                memo[i][mask]=res;
            return res;
        };
        //初始有限制
        return f(0,0,true,false);
    }
};

2719. 统计整数数目

题目描述

描述:给你两个数字字符串 num1 和 num2 ,以及两个整数 max_sum 和 min_sum 。如果一个整数 x 满足以下条件,我们称它是一个好整数:

num1 <= x <= num2
min_sum <= digit_sum(x) <= max_sum.
请你返回好整数的数目。答案可能很大,请返回答案对 109 + 7 取余后的结果。

注意,digit_sum(x) 表示 x 各位数字之和。

示例 1:

输入:num1 = "1", num2 = "12", min_num = 1, max_num = 8
输出:11
解释:总共有 11 个整数的数位和在 1 到 8 之间,分别是 1,2,3,4,5,6,7,8,10,11 和 12 。所以我们返回 11 。

示例 2:

输入:num1 = "1", num2 = "5", min_num = 1, max_num = 5
输出:5
解释:数位和在 1 到 5 之间的 5 个整数分别为 1,2,3,4 和 5 。所以我们返回 5 。

提示:

1 <= num1 <= num2 <= 1022
1 <= min_sum <= max_sum <= 400

解题思路

class Solution {
public:
        const int MOD=1e9+7;
        int f(string s,int min_sum,int max_sum)
        {
            int n=s.length();
            //截止每一位的sum
            int memo[n][min(9*n,max_sum)+1];
            memset(memo,-1,sizeof(memo));
            //前导0无影响 012和12和一样
            function<int(int,int,bool)> f=[&](int i,int sum,bool is_limit)->int{
                if(sum>max_sum) return 0;
                if(i==n) return sum>=min_sum;
                if(!is_limit&&memo[i][sum]!=-1)
                    return memo[i][sum];
                int res=0;
                int up=is_limit?s[i]-'0':9;
                for(int d=0;d<=up;d++)
                    res=(res+f(i+1,sum+d,is_limit&&d==up))%MOD;
                if(!is_limit)
                    memo[i][sum]=res;
                return res;
            };
            return f(0,0,true);
        }
    int count(string num1, string num2, int min_sum, int max_sum) {
        //计算<=num2的合法数字个数a
        //计算<=num1-1的合法数字个数b
        //那么答案就是a-b
        //考虑到num1是一个字符串 故可直接计算<=num1的合法数字个数 再单独判断num1这个数是否合法
        int ans=f(num2,min_sum,max_sum)-f(num1,min_sum,max_sum)+MOD;  //+MOD避免负数 取模后大小不一定
        int sum=0; 
        for(char c:num1)
            sum+=c-'0';
        ans+=min_sum<=sum&&sum<=max_sum;
        return ans%MOD;
    }
};

总结:数位DP模板,还不太熟练hhh,先记下来,慢慢熟悉咯!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值