题目描述:
定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部,如把字符串abcdef左旋转2位得到字符串cdefab。
请实现字符串左旋转的函数,要求对长度为n的字符串操作的时间复杂度为O(n),空间复杂度为O(1)。
一、指针翻转法
咱们先来看个例子,如下:abc defghi,若要让abc移动至最后的过程可以是:abc defghi->def abcghi->def ghiabc
如此,我们可定义俩指针,p1指向ch[0],p2指向ch[m];
一下过程循环m次,交换p1和p2所指元素,然后p1++, p2++;。
- 第一步,交换abc 和def ,abc defghi->def abcghi
- 第二步,交换abc 和 ghi,def abcghi->def ghiabc
整个过程,看起来,就是abc 一步一步 向后移动
- abc defghi
- def abcghi
- def ghi abc
//最后的 复杂度是O(m+n)
由上述例子九个元素的序列abcdefghi,您已经看到,m=3时,p2恰好指到了数组最后一个元素,于是,上述思路没有问题。但如果上面例子中i 的后面还有元素列?
例子:
def ghi abc jk
当p1指向a,p2指向j时,由于p2+m越界,那么此时p1,p2不要变
这里p1之后(abcjk)就是尾巴,处理尾巴只需将j,k移到abc之前,得到最终序列,代码编写如下:
01.//copyright@July、颜沙
02.//最终代码,July,updated again,2011.04.17。
03.#include <iostream>
04.#include <string>
05.using namespace std;
06.
07.void rotate(string &str, int m)
08.{
09.
10. if (str.length() == 0 || m <= 0)
11. return;
12.
13. int n = str.length();
14.
15. if (m % n <= 0)
16. return;
17.
18. int p1 = 0, p2 = m;
19. int k = (n - m) - n % m;
20.
21. // 交换p1,p2指向的元素,然后移动p1,p2
22. while (k --)
23. {
24. swap(str[p1], str[p2]);
25. p1++;
26. p2++;
27. }
28.
29. // 重点,都在下述几行。
30. // 处理尾部,r为尾部左移次数
31. int r = n - p2;
32. while (r--)
33. {
34. int i = p2;
35. while (i > p1)
36. {
37. swap(str[i], str[i-1]);
38. i--;
39. }
40. p2++;
41. p1++;
42. }
43. //比如一个例子,abcdefghijk
44. // p1 p2
45. //当执行到这里时,defghi a b c j k
46. //p2+m出界 了,
47. //r=n-p2=2,所以以下过程,要执行循环俩次。
48.
49. //第一次:j 步步前移,abcjk->abjck->ajbck->jabck
50. //然后,p1++,p2++,p1指a,p2指k。
51. // p1 p2
52. //第二次:defghi j a b c k
53. //同理,此后,k步步前移,abck->abkc->akbc->kabc。
54.}
55.
56.int main()
57.{
58. string ch="abcdefghijk";
59. rotate(ch,3);
60. cout<<ch<<endl;
61. return 0;
62.}
二、三步翻转法
将一个字符串分成两部分,X和Y两个部分,在字符串上定义反转的操作X^T,即把X的所有字符反转(如,X="abc",那么X^T="cba"),那么我们可以得到下面的结论:(X^TY^T)^T=YX。显然我们这就可以转化为字符串的反转的问题了。
不是么?ok,就拿abcdef 这个例子来说,若要让def翻转到abc的前头,那么只要按下述3个步骤操作即可:
1、首先分为俩部分,X:abc,Y:def;
2、X->X^T,abc->cba, Y->Y^T,def->fed。
3、(X^TY^T)^T=YX,cbafed->defabc,即整个翻转。
//Copyright@ 小桥流水 && July
//c代码实现,已测试正确。
//http://www.smallbridge.co.cc/2011/03/13/100%E9%A2%98
//_21-%E5%B7%A6%E6%97%8B%E8%BD%AC%E5%AD%97%E7%AC%A6%E4%B8%B2.html
//July、updated,2011.04.17。
#include <stdio.h>
#include <string.h>
char * invert(char *start, char *end)
{
char tmp, *ptmp = start;
while (start != NULL && end != NULL && start < end)
{
tmp = *start;
*start = *end;
*end = tmp;
start ++;
end --;
}
return ptmp;
}
char *left(char *s, int pos) //pos为要旋转的字符个数,或长度,下面主函数测试中,pos=3。
{
int len = strlen(s);
invert(s, s + (pos - 1)); //如上,X->X^T,即 abc->cba
invert(s + pos, s + (len - 1)); //如上,Y->Y^T,即 def->fed
invert(s, s + (len - 1)); //如上,整个翻转,(X^TY^T)^T=YX,即 cbafed->defabc。
return s;
}
int main()
{
char s[] = "abcdefghij";
puts(left(s, 3));
return 0;
}
以上两个均符合复杂度的要求。