codility上的问题(13) MU 2011

本文介绍了一种高效算法,用于计算0至N范围内整数中数字0的出现次数,采用O(L)的时间复杂度和空间复杂度,其中N以字符串形式给出。

给定正整数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;        
        
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值