数位dp

数字1的个数

如果计算区间[l,r]中出现次数,cnt(r) - cnt(l - 1)即可

class Solution {
public:
    int res[10][1024];
    int countDigitOne(int n) {
        return dp(0, 0, true, false, to_string(n));
    }
    // 模板 有4个参数
    // 从i开始填数字,i前面填过的数字集合是mask
    // isLimit表示前面填的数字是否都是n对应位上的:isLimit==true,那么当前位至多为s[i](eg:n=123,前两位填的数字都是n对应位上的填了12,那么当前位只能填0~s[i]即0~3了);isLimit==false,当前位可以从0~9选填,至多为'9'
    // isNum表示前面是否填了数字(是否跳过,是否是一个合法数字 比如010就不合法):isNum==true,那么当前位可以从0开始;isNum==false,那么我们可以跳过这位也不填,或者这位从1~9选填
    int dp(int i, int mask, bool islimit, bool isnum, string n)
    {
	//返回条件需要视题目情况而定,这里返回mask是因为用的mask存的1的个数
        if(i == n.size()) return mask;
		
	// 前面既没有填n对应位上的数,又填了数,那么后面填的都不受限,这种情况会重复计算,所以用记忆化搜索来存
        if(!islimit && isnum && res[i][mask] != 0)
            return res[i][mask];
			
        int sum = 0;
		
	//如果之前位没有填数字,那么当前位可以选择跳过
        if(!isnum)
            sum = dp(i + 1, mask, false, false, n);
			
	//确定每一位的上界up
        int up = 0;
        if(islimit)
            up = n[i] - '0';
        else up = 9;
	
	//如果前一位没有填数字(isnum == false),当前位不能填0,从1开始;否则可从0开始填
        for(int j = 1 - int(isnum); j <= up; j ++)
        {
            sum += dp(i + 1, mask + (j == 1), islimit && j == up, true, n);
        }
		
	//记忆化搜索,存储当前状态(i, mask)的值
        res[i][mask] = sum;
        return sum;
    }
};
posted @ 2023-04-21 15:58  bothgone  阅读(25)  评论(0)    收藏  举报