codility上的问题(13) MU 2011

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


你可能感兴趣的:(算法,codility)