首页 > 代码库 > [算法]字符串左移k位

[算法]字符串左移k位

如,abcde左移3位为deabc

要求时间复杂度O(n),空间复杂度O(1),每一个字符只能遍历一次

 

摘自http://blog.csdn.net/geniusluzh/article/details/8460031

利用数学解决该问题

    其实对于这道题,最初一看的想法就是将当前位依次替换左移m位对应的那个位,然后依次替换。后来发现有的情况一次循环替换就能全部完成整个串的左移,而有的情况下会出现多个循环链,一时只得到规律,不能想到很好的证明办法,只怪以前初等数论没有好好学啊!

    我们发现对于长度为n的串,左移m位,会形成gcd(n, m)个链,这里的gcd就是大家熟知的最大公约数,每个链的长度显然就是n/(gcd(n, m))。我们如何来证明这个问题呢?我们令i+j*m 和 i+k*m正好走了一圈又指向同一个元素,那么便是(i+j*m)%n == (i+k*m)%n,因此根据同余的性质我们知道n | (k-j)*m,而n = n‘ * gcd(n, m), m = m‘ * gcd(n, m)。所以我们得到n‘ | (k-j) * m‘, 由于n‘ 和 m‘ 互素,因此 n‘ | (k - j),因此我们知道最小的k-j = n‘ = n/gcd(n, m),也就是说一个循环链里头有n/gcd(n, m)个元素,因此总共有gcd(n, m)个循环链,于是得证。因此我们只需要取前gcd(n, m)个元素,每个走一条链进行循环的替换就行了。其实这个算法是最优的,真正的O(N)的时间复杂度,而且没有额外的开销。

 

int gcd(int m, int n){    if(m < n){        int t = m;        m = n;        n = t;    }    int r = m%n;    while(r){        m = n;        n = r;        r = m%n;            }    return n;} char* leftShift(char* str, int n, int k){    char s, t;    int g = gcd(n,k),len = n / g;    for(int i = 0; i < g; ++i){        s = str[i];        for(int j = 0; j < len; ++j){            t = str[(n - k + i)%n];            str[(n - k + i)%n] = s;            s = t;            i = (n - k + i)%n;                times++;        }            }    return str;}

 

知识共享许可协议
本文基于知识共享署名-非商业性使用 3.0 许可协议进行许可。欢迎转载、演绎,但是必须保留本文的署名林羽飞扬,若需咨询,请给我发信