这道题目与leetcode 600 有一些相似, 所以博主一开始用的是相类似的解法. dp[n][i]记录的是位数<=n的数中, 包含i个1的数的个数.
1. dp[n][0] = dp[n-1][0] + 8*dp[n-1][0] = dp[n-1][0] * 9.
其中, 第一部分表示位数<=n-1的数中, 包含0个1的个数. 第二部分表示位数等于n的数, 包含0个1的个数. 第n位可以选择2, 3, …9这八个数.
2. dp[n][i] = dp[n-1][i] + dp[n-1][i-1] + 8*dp[n-1][i]
其中第一部分表示位数小于等于n-1的包含i个1的数的数量. 第二部分表示位数为n, 且第n位为1. 则剩下的部分只要满足有[i-1]个1就可以了. 第三部分表示位数也是n位. 但是第n位选择2, 3, … 9
假设n的位数为N,在得到这个dp之后我们可以把问题拆解为两部分: 位数为N的数中包含的1数量. 位数小于N的数中包含的1的数量. 第二部分很好求:
for(int c = len-1; c>=1; --c) {
cnt += dp[len-1][c]*c;
}
zhzz至于第一部分, 因为必须<=n, 所以我们需要逐一位处理:
先处理小于n的数,
考虑第N位:
5314
4xxx
3xxx
2xxx
1xxx
第N位可以取4, 3, 2, 1
剩下的位:
5314
52xx
51xx
50xx
第N-1可以取2, 1, 0
int countDigitOne(int n) {
if(0 == n) {
return 0;
}
int len = 1;
long mask = 10;
while(n >= mask) {
++len;
mask *= 10;
}
vector<vector<int>> dp(len+1, vector<int>(len+1, 0));
dp[0][0] = 1;
dp[1][0] = 9;
dp[1][1] = 1;
for(int l = 2; l <= len; ++l) {
dp[l][0] = dp[l-1][0] * 9;
for(int c = 1; c <= l; ++c) {
dp[l][c] = dp[l-1][c] * 9 + dp[l-1][c-1];
}
}
mask /= 10;
int cnt = 0;
/*size = len*/
int prev_1_cnt = 0;
for(int i = len; i >= 1; --i) {
const int digit = n/mask;
int not1_head = 0;
if(i == len) {
not1_head = digit - 2;
}else if(digit == 1){
not1_head = 1;
}else {
not1_head = digit - 1;
}
if(not1_head >= 1) {
for(int l = 0; l <= i-1; ++l) {
cnt += not1_head * dp[i-1][l] * (prev_1_cnt+l);
}
}
if(digit > 1) {
for(int c = 0; c <= i-1; ++c) {
cnt += dp[i-1][c] * (prev_1_cnt + c +1);
}
}
n = n % mask;
mask /= 10;
if(digit == 1) {
++prev_1_cnt;
}
}
/*size < len*/
for(int c = len-1; c>=1; --c) {
cnt += dp[len-1][c]*c;
}
cnt += prev_1_cnt;
return cnt;
}
a前面的方法还是非常繁琐的. 这里再引入StefanPochmann 大神的解法
对于一个数. 我们计算能有多少个数个位上有1, 十位上有1, 百位上有1…把这些数都加起来就是我们要的结果:
a我们只分析百位上的情况:
n=3141592
3141592
0-3141|1|0-99
总共有(3141+1)*100个数
n=3141192
3141192
0-3140|1|0-99
3141|1|0-91
总共有3141*100 + 92个
能n=3141092
3141092
0-3140|1|0-99
共有3141*100个
int countDigitOne(int n) {
if(0 == n) {
return 0;
}
long mask = 1;
int cnt = 0;
while(n >= mask) {
const int a = n / mask;
const int b = n % mask;
const int v = a%10;
if(v > 1) {
cnt += (a/10 + 1) * mask;
}else if(v == 1) {
cnt += (a/10) * mask + b+1;
}else {
cnt += (a/10) * mask;
}
mask *= 10;
}
return cnt;
}