题目描述
求出1 ~ 13的整数中1出现的次数,并算出100 ~ 1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
算法
(数位计算)
O
(
l
o
g
n
)
O(logn)
O(logn)
(1)计算每一位上的1出现的个数(从0到n)
(2) 总共循环
l
o
g
n
logn
logn次,即n的位数
算法思路图解
例如下图是计算
c
c
c 这一位上(从0到n)出现1的个数
时间复杂度是 O ( l o g n ) O(logn) O(logn):下面代码时间复杂度是 O ( l o g 2 n ) O(log^2n) O(log2n),但其中一个是不会大于32的;另外我们还可以预处理left和right
空间复杂度是 O ( l o g n ) O(logn) O(logn):需要answer数组存数位信息
进阶题目:AcWing 338.计数问题 (类似整数中1出现的次数 数位统计DP C++)
C++代码1
class Solution {
public:
int NumberOf1Between1AndN_Solution(int n) {
if (!n) return 0;
// n = 123 answer = [3, 2, 1]
vector<int> answer;
while(n) answer.push_back(n % 10), n /= 10;
int res = 0;
for (int i = answer.size() - 1; i >= 0; i --) {
int left = 0, right = 0, t = 1;
for (int j = answer.size() - 1; j > i; j --) left = left * 10 + answer[j];
for (int j = i - 1; j >= 0; j --) right = right * 10 + answer[j], t *= 10;
res += left * t;
if (answer[i] == 1) res += right + 1;
if (answer[i] > 1) res += t;
}
return res;
}
};
2021/2/21 更新
class Solution {
public:
int NumberOf1Between1AndN_Solution(int n) {
vector<int> digit;
// n: 123456
while (n) {
digit.push_back(n % 10);
n /= 10;
}
reverse(digit.begin(), digit.end());
// digit: [1, 2, 3, 4, 5, 6]
int sz = digit.size();
int res = 0;
for (int i = 0; i < sz; i ++) {
int left = 0;
for (int k = 0; k < i; k ++) left = left * 10 + digit[k];
int right = 0, t = 1;
for (int k = i + 1; k < sz; k ++) {
right = right * 10 + digit[k];
t = t * 10;
}
int c = digit[i];
res += left * t;
if (c == 0) res += 0;
else if (c == 1) res += right + 1;
else if (c > 1) res += t;
}
return res;
}
};
C++ 代码2 暴力做法
class Solution {
public:
int countOne(int n) {
int ans = 0;
while(n) {
if (n % 10 == 1) ans ++;
n /= 10;
}
return ans;
}
int NumberOf1Between1AndN_Solution(int n)
{
int res = 0;
for (int i = 1; i <= n; i ++) {
res += countOne(i);
}
return res;
}
};
写在最后:我的博客主要是对计算机领域所学知识的总结、回顾和思考,把每篇博客写得通俗易懂是我的目标,分享技术和知识是一种快乐 ,非常欢迎大家和我一起交流学习,有任何问题都可以在评论区留言,也期待与您的深入交流(^∀^●)