给定正整数N,求0..N的整数中出现多少次0。
N是以字符串形式给的,长度L [1..10^4]
结果对1410000017取模。
要求时间复杂度O(L),空间复杂度O(L)。
这个题有两个比较恶心的地方,第一个是求的不是1..N之中的0的次数,而是0..N的,第二那个模的2倍会超过int,这就使得算加法时也要小心。
算法不难:
设当前考虑的是某一位,
xxxxxoxxxx
它之前那些位不含它本身 记做pre[i],它之后的那些位记做suf[i]。
那么pre[i]表示的数 要从1变到(pre[i] - 1),第i位可以为0,suf[i]那么多位可以任意,所以这些数总个数是(pre[i] - 1) * w[i],其中w[i]是第i为后面的位数m的权重,即10^m。
然后,当前面那些位恰好是pre[i]时,考虑第i位,如果等于0,说明后面那些位只能全0变为suf[i],所以有(suf[i] + 1)个。
否则如果第i位大于0,则说明pre[i]和0这些前缀经过了后面完整的一个周期,因此加上一个w[i]。
对所有的i慢慢做就可以了,注意i从1开始,因为首位不能为0,这是0的特殊性决定的。
如果求的是其他数字的话,i要从第0位开始算。
代码:
// you can also use includes, for example:
// #include <algorithm>
#include <vector>
const int M = 1410000017;
int add(long long x,long long y) {
return ((x += y) >= M)?(x - M):x;
}
int mul(long long x, long long y) {
return x * y % M;
}
int solution(const string &S) {
// write your code here...
int n = S.size(),answer,i;
vector<int> pre, suf, w;
pre.resize(n);
suf.resize(n);
w.resize(n);
pre[0] = 0;
for (i = 1; i < n; ++i) {
pre[i] = add(mul(pre[i - 1], 10) , S[i - 1] - '0');
}
w[n - 1] = 1;
suf[n - 1] = 0;
for (i = n - 2; i >= 0; --i) {
w[i] = mul(w[ i + 1], 10);
suf[i] = add(suf[i + 1], mul(w[i + 1], S[i + 1] - '0'));
}
for (i = answer = 1; i < n; ++i) {
answer = add(answer, mul(pre[i], w[i]));
if ((answer -= w[i]) < 0) {
answer += M;
}
if (S[i] > '0') {
answer = add(answer,w[i]);
}
else if (S[i] == '0') {
answer = add(answer, suf[i]);
answer = add(answer, 1);
}
}
return answer;
}
本文介绍了一种高效算法,用于计算0至N范围内整数中数字0的出现次数,采用O(L)的时间复杂度和空间复杂度,其中N以字符串形式给出。

被折叠的 条评论
为什么被折叠?



