(7)左旋字符串


原作者:v_JULY_vhttp://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 >= n
	if(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 --;
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值