首页 > 代码库 > 程序员编程技术学习笔记——左旋转字符串

程序员编程技术学习笔记——左旋转字符串

程序员编程技术学习笔记——左旋转字符串

1.    题目描述

    给定一个字符串,要求把字符串前面的若干个字符移动到字符串的尾部,如把字符串“abcdef”前面的2个字符‘a‘和‘b‘移动到字符串的尾部,使得原字符串变成字符串“cdefab”。请写一个函数完成此功能,要求对长度为n的字符串操作的时间复杂度为 O(n),空间复杂度为 O(1)

2.    解法1:暴力左移

    这个解法简单粗暴易想!你不是要以为k个字符吗,我先移动一位,然后把移动一位的函数运行k次就好啦~~其中移位1位的程序也好写,取首位字符,转存,后面n-1移位,转存字符放最后,即可。

我们用下面的图示来帮助说明:


 

这种解法的算法思想和流程步骤我们都说好了。我们来分析一下它的复杂度:时间复杂度O(kn),空间复杂度O(1)。哎呀,时间太长了!不符合题目的要求,难怪叫做暴力左移……不管怎样,我们还是要给出这种解法的实现代码的。

 

#include <iostream>

using namespace std;

string leftShift1(string str, int n, int k)
{
    char temp;
    int i;
    while(k--)  //移位k次
    {
        temp=str[0];
        for(i=1; i<n; i++)
        {
            str[i-1]=str[i];
        }
        str[n-1]=temp;
    }
    return str;
}

int main()
{
    string str="abcdef";
    int n=str.length();  //串长
    int k=2;  //需要左移的位数

    if(k>=n) k%=n;  //考虑健壮性
    str=leftShift1(str, n, k);
    cout<<str<<endl;

    return 0;
}

3.  解法2:挥霍空间

    你听这种方法的名字就知道,这种方法的空间复杂度应该比较高。算法其实也很简单:新建一个等长的字符数组strArray,先把前面的k位字符复制到strArray的最后k位,类似复制后面n-k位,即可。

如下图所示:


这种方法的空间复杂度为O(n),时间复杂度为O(n),时间复杂度符合要求,当时空间复杂度不行。实现代码如下:

 

string leftShift2(string str, int n, int k)
{
    string str2="";
    str2=str2+str.substr(k, n-k);  //用到了string类型的+连接和substr取子串
    str2=str2+str.substr(0, k);
    return str2;
}

4.    解法3:手摇法

    这种叫做手摇法,那么什么是手摇法呢?july博客上给出了图示:

很神奇吧~~这个过程是不是和字符串翻转很像呢~

    上面这个过程数学化为:

    将一个字符串分成X和Y两个部分,在每部分字符串上定义反转操作,如X^T,即把X的所有字符反转(如,X="abc",那么X^T="cba"),那么就得到下面的结论:(X^TY^T)^T=YX,显然就解决了字符串的反转问题。

    例如,字符串 abcdef ,若要让def翻转到abc的前头,只要按照下述3个步骤操作即可:

    首先将原字符串分为两个部分,即X:abc,Y:def;

    将X反转,X->X^T,即得:abc->cba;将Y反转,Y->Y^T,即得:def->fed。

    反转上述步骤得到的结果字符串X^TY^T,即反转字符串cbafed的两部分(cba和fed)给予反转,cbafed得到defabc,形式化表示为(X^TY^T)^T=YX,这就实现了整个反转。

    我们也用下面的图示说明:

 

    反转的过程也较简单,例如从head到tail是需要反转的字符串,我们先把head给temp,然后tail给head,head++,temp给tail,tail--,一直运行到head>tail即可。

    由此可见,该方法的空间复杂度为O(1),时间复杂度为O(n+n)=O(n)。为啥是O(n+n)呢?因为这里有3次反转,前两次加起来为O(n),第三次是全部长度反转,为另一个O(n),这样即可。

    下面我们给出代码:

 

string ReverseString(string str,int head,int tail)
{
    while (head < tail)
    {
        char t = str[head];
        str[head++] = str[tail];
        str[tail--] = t;
    }
    return str;
}


string leftShift3(string str, int n, int k)
{
    k %= n;               //若要左移动大于n位,那么和%n 是等价的
    str=ReverseString(str, 0, k - 1); //反转[0..k - 1],套用到上面举的例子中,就是X->X^T,即 abc->cba
    str=ReverseString(str, k, n - 1); //反转[k..n - 1],例如Y->Y^T,即 def->fed
    str=ReverseString(str, 0, n - 1); //反转[0..n - 1],即如整个反转,(X^TY^T)^T=YX,即 cbafed->defabc。
    return str;
}

5.    举一反三

    1)、链表翻转。给出一个链表和一个数k,比如,链表为1→2→3→4→5→6,k=2,则翻转后2→1→6→5→4→3,若k=3,翻转后3→2→1→6→5→4,若k=4,翻转后4→3→2→1→6→5,用程序实现。

    2)、编写程序,在原字符串中把字符串尾部的m个字符移动到字符串的头部,要求:长度为n的字符串操作时间复杂度为O(n),空间复杂度为O(1)。 例如,原字符串为”Ilovebaofeng”,m=7,输出结果为:”baofengIlove”。

    3)、单词翻转。输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变,句子中单词以空格符隔开。为简单起见,标点符号和普通字母一样处理。例如,输入“I am a student.”,则输出“student. a am I”。

 


程序员编程技术学习笔记——左旋转字符串