左旋字符串的一种野路子解法,时间复杂度O(2N)

来源:互联网 发布:linux退出vi模式 编辑:程序博客网 时间:2024/04/30 03:39
* 题目:定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部。 
* 如把字符串abcdef左旋转2位得到字符串cdefab。 

* 请实现字符串左旋转的函数。要求时间对长度为n的字符串操作的复杂度为O(n),辅助内存为O(1)。


据说是微软面试题,想挑战一把自己,于是不看答案闷头想了两天,得到一次优解-_-!

题目可抽象成:数组由
1  2  3  ...   N ... L

N+1  N+2   ....   L  1  2  3  ... N
的变化
由于空间复杂度为O(1),所以不能开辟arr_new,并让arr_new[i] = arr_old[(i+N+1)%L]
又由于时间复杂度为O(N),所以不能定义函数leftTurn1elem(),再循环调用N次,这就变成O(N^2)了
所以必须是某种swap,一步到位的swap,但又不能是对等的swap,而是麻花债式的swap,就是a要占b的空间,b转而去占c的空间,以此类推


那怎么保证麻花债的终止呢?我想了很多办法,最终决定用flag,表示该元素的空间已被侵占了,不用再去讨债了。
有人要问,每个数组元素配一个flag,空间占用还是O(N)啊!问得好,我这里用了个trick,将每个字符的最高位(ASCII用不到)当做flag。
最后,怎么保证N个元素都迁移了呢?用for循环,每个元素都apply上述步骤,对于N > L/2的情况,会出现前一轮麻花债影响后一轮的情况,好办,for循环也检测是否已侵占,若是则continue跳过。

#include <iostream>#include <cstring>using namespace std;bool Visited(char *a){    return (((*a & 0x80) != 0) ? true : false);}void Visit(char *a){    *a = *a | 0x80;}void deVisit(char *a){    *a = *a & 0x7f;}void leftturn(char *p, int len, int turn){    if (turn < 1 || turn > len)    {        cout << "wrong input" << endl;        return;    }    char tmpSave, tmpRestore;    int srcPos, dstPos;    bool firstVisit;    for (int c = 0; c < turn; c++)    {        if (Visited(&p[c]))            continue;        srcPos = c;        firstVisit = true;        while(1){            dstPos = srcPos - turn < 0 ? srcPos - turn + len : srcPos - turn;            if (Visited(&p[dstPos]))                break;            tmpSave = p[dstPos];            if (firstVisit)            {                p[dstPos] = p[srcPos];                firstVisit = false;            }else{                p[dstPos] = tmpRestore;            }            Visit(&p[dstPos]);            srcPos = dstPos;            tmpRestore = tmpSave;        }    }    for (int i = 0; i < len; i++)        deVisit(&p[i]);}char a[] = "wanghaipeng";int main(){    cout << a << endl;    leftturn(a, strlen(a), 2);    cout << a << endl;    return 0;}


0 0
原创粉丝点击