首页 > 代码库 > [算法]字符串左移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 许可协议进行许可。欢迎转载、演绎,但是必须保留本文的署名林羽飞扬,若需咨询,请给我发信