(7)左旋字符串

来源:互联网 发布:电子相框 知乎 编辑:程序博客网 时间:2024/06/08 19:32


原作者:v_JULY_v:http://blog.csdn.net/v_july_v/article/details/6322882

                         何海涛--《剑指offer》

感谢上述作者;

我仅仅是将其一些思路重新自己实现了一遍,稍作整理,以便经常温习用。


题目描述:

字符串左旋操作:将一个字符串前面的若干个字符移动到字符串尾部;例如字符串“abcdefgi” ,左旋3位,结果为“defgiabc”;

请实现字符串左旋转的函数,要求对长度为n的字符串操作时间复杂度为O(n) , 空间复杂度为O(1);


假设字符串长度:n

左旋移位:m

思路一:蛮力移位

每一移位一位:利用temp保存字符串的第一位string[0],并将后面的字符赋值给前一位,即string[i - 1] = string[i];再将temp赋值给string[n-1];

重复操作m次:

辅助空间:temp


思路二:利用指针左旋

字符串string[0] ------string[n-1] ; 指针p1 指向string[0] , p2指向string[m]  ,即 相隔m位;(n > m)

交换swap(string[p1] , string[p2]) ,然后p1++ ,p2++ ,循环操作m次;

若 n % m == 0:即 n 整除 m时:

例如"123456789" ,左旋3位--》变成“456789123” ;此时 9 % 3 == 0;最终 p1 指向9 , p2指向3;

若当(n % m != 0)时:

"12345678967" -->"45678912367"时, 尾部段落“12367” , p1指向1 , p2指向6;此时无法正常的完成全部m位左旋;

我们知道string[i] ,0 <= i <= n - 1;p1指向1时,其代表下标值为6,p2指向6,其下标值为9;9 + (m - 1) = 9 + (3 - 1) = 11 > n - 1 = 10 出界;

对于尾部段落“12367” ,我们只要将“6" ,“7”一步步左移到"123"前面即可。且移动元素个数=  n - p2 = 11 - 9 = 2;

算法步骤:

1、首先让p1=string[0],p2=string[m],即让p1,p2相隔m的距离;

2、判断p2+m-1是否越界,如果没有越界转到3,否则转到4;

3、不断交换*p1与*p2,然后p1++,p2++,循环m次,然后转到2。

4、此时p2+m-1 已经越界,在此只需处理尾巴。过程如下:

   4.1 通过n-p2得到p2与尾部之间元素个数r,即我们要前移的元素个数。

   4.2 以下过程执行r次:

      string[p2]<->string[p2-1],string[p2-1]<->string[p2-2],....,string[p1+1]<->string[p1];p1++;p2++;


其中  k = (n - m) - n % m;k为总的正常左旋移动次数;

          r = n - p2 ; 越界时,处理尾巴需要移动的元素个数

          p2 + m - 1 用于判断是否越界(n - 1);


思路三:递归转换法

    就是说,把一个规模为N的问题化解为规模为M(M<N)的问题。
    举例来说,设字符串总长度为L,左侧要旋转的部分长度为s1,那么当从左向右循环交换长度为s1的小段,直到最后,由于剩余的部分长度为s2(s2==L%s1)而不能直接交换。

    该问题可以递归转化成规模为s1+s2的,方向相反(从右向左)的同一个问题。随着递归的进行,左右反复回荡,直到某一次满足条件L%s1==0而交换结束。

 举个具体事例说明,如下:

1、对于字符串abc def ghi gk,

将abc右移到def ghi gk后面,此时n = 11,m = 3,m = n % m = 2;

abc def ghi gk -> def ghi abc gk

2、问题变成gk左移到abc前面,此时n = m + m = 5,m = 2,m = n % m 1;

abc gk -> a gk bc

3、问题变成a右移到gk后面,此时n = m + m = 3,m = 1,m = n % m = 0;

a gk bc-> gk a bc。 由于此刻,n % m = 0,满足结束条件,返回结果。

 

    即从左至右,后从右至左,再从左至右,如此反反复复,直到满足条件,返回退出



思路四:三步翻转法



举例:“abcdefgij”,左旋3位;

(1)

我们将其分为两部分:“abc” ,“defgij”;

先对子部分翻转:“abc”-->"cba" ; “defgij”-->"jigfed";

两者结合:“cbajigfed”;再对其翻转"cbajigfed" ----->"defgijabc";

(2)

也可以先对总的字符串翻转:“abcdefgij”---->"jigfedcba"

再对子部分翻转,不过这次分割子部分时:从最右边选择m位为一部分--》“cba”,左端剩余n-m位为一部分--》"jigfed"

翻转:“jigfed”-->"defgij" ,“cba”--"abc"

 最终“defgijabc"


代码实现:

#include<iostream>#include<string>using  namespace std;//---------------------------------------------左旋字符串------------------------------------------------------------void leftRotateOne(char* pStr , int length);//暴力void Roate(char *pHead, char *pTail);//-----------------------------------------------方法一暴力移位------------------------------------------------------void leftRoate_Soulation1(char* pStr , int length ,int m){if(pStr == NULL || m < 0)return;while(m--){leftRotateOne(pStr , length);}}void leftRotateOne(char* pStr , int length){char temp = pStr[0];for(int i = 1; i < length; i++)pStr[i-1] = pStr[i];pStr[length - 1] = temp;}//-----------------------------------------------方法二指针翻转-----------------------------------------------------void leftRoate_Soulation2(string &pStr , int m){if(pStr.length() <= 0 || m <= 0)return;int n = pStr.length();//m >= nif(m % n <= 0)return;int p1 = 0;int p2 = m;int k = (n - m) - n%m;for(int i = 0; i < k; i++){swap(pStr[p1] , pStr[p2]);p1++;p2++;}int r = n - p2;while(r--){int i = p2;while(i > p1){swap(pStr[i] , pStr[i - 1]);i--;}p2++;p1++;}}//-----------------------------------------------方法三 递归法--------------------------------------------------------void leftRoate_Soulation3(string &pStr , int length , int m , int head , int tail , bool flag){if(length <= 0 ||head == tail || m <= 0)return;//左旋if(flag == true){int p1 = head;int p2 = head + m;int n = length;int k = (n - m) - (n%m);for(int i = 0; i < k; i++){swap(pStr[p1] , pStr[p2]);p1++;p2++;}leftRoate_Soulation3(pStr , n - k , n%m , p1 , tail , false);}else{int p1 = tail;int p2 = tail - m;int n = length;int k = (n - m) - n%m;for(int i = 0; i < k; i++){swap(pStr[p1] , pStr[p2]);p1--;p2--;}leftRoate_Soulation3(pStr , n - k , n%m , head , p1 , true);}}//------------------------------------------------方法四三步旋转法------------------------------------------------char* leftRoate_Soulation4(char* pStr, int m){    if(pStr != NULL)    {        int nLength = static_cast<int>(strlen(pStr));        if(nLength > 0 && m > 0 && m < nLength)        {            char* pFirstStart = pStr;            char* pFirstEnd = pStr + m - 1;            char* pSecondStart = pStr + m;            char* pSecondEnd = pStr + nLength - 1;            // 翻转字符串的前面n个字符            Roate(pFirstStart, pFirstEnd);            // 翻转字符串的后面部分            Roate(pSecondStart, pSecondEnd);            // 翻转整个字符串            Roate(pFirstStart, pSecondEnd);        }    }    return pStr;}void Roate(char *pHead, char *pTail){    if(pHead == NULL || pTail == NULL)        return;    while(pHead < pTail)    {        char temp = *pHead;        *pHead = *pTail;        *pTail = temp;        pHead ++, pTail --;    }}


原创粉丝点击