左旋转字符串
题目描述:
对一个字符串,例如"abcdefgh", 要求对前三个字符即"abc"进行左转,生成型如"defghabc"结果的字符串。(m: 要左转字符个数; n:原字符长度)
要求:
时间复杂度为O(n), 空间复杂度为O(1).
下面介绍解决该问题的三种方法:
1.暴力位移法
很容易想到的一种方法就是将要左转的子字符串存储在另外一个数值(或指针)中,再利用循环将剩下的字符逐个向左移动直到数值头部为止,最后将左移的子字符串复制到原字符串的尾部. 该方法需要额外的m个存储空间,字符左移时间复杂度为O(n-m), 左移子字符串时间复杂度为O(m), 所以总的时间复杂度为O(n).
实现代码如下: (已经过验证)
1 /** 2 常规暴力位移法: 3 strlength: 原字符串长度(n); rotalength: 左移字符个数(m) 4 **/ 5 void rotation1(char *stringword, int strlength, int rotalength) 6 { 7 char temp[20]; 8 int i,j; 9 // 将左转字符存储在新字符数组中 10 for(i=0;i<rotalength;i++) 11 { 12 temp[i]=stringword[i]; 13 } 14 // 对剩下的字符进行左移 15 for(j=0;j<strlength-rotalength;j++) 16 { 17 stringword[j]=stringword[rotalength+j]; 18 } 19 // 复制左转字符 20 for(i=0;i<rotalength;i++) 21 { 22 stringword[j+i]=temp[i]; 23 } 24 }
2. 递归位移法
先来看一个图2-1:
图2-1: 递归位移步骤示意图
蓝色区域表示的是左转字符串部分,黄色区域表示的是和左转字符数相同的且相邻子字符串。对蓝色区域和黄色区域进行替换(利用双指针同时移动实现替换),可以看到左转字符串后移,这样一直重复进行下去。会出现两种结果:(1) n%m=0, 即原字符串长度是左移字符串长度的整数倍,那么你n/m次互换后即可得到结果。(2)n%m!=0,例如图2-1中的final,剩余的字符小于左转字符长度。这是的一种想法是将剩余部分和左转字符串看成一个新的字符串,而且剩余字符串变成"左转字符串",而原左转字符串变成“剩余部分”,这时要求对字符串进行右转操作。如图2-1的括号所示。这样就可以利用递归的思想。(对字符串进行移动后,剩余部分的字符串长度一定小于左转(右转)字符串长度,随着次数的增加,左转(右转)字符串长度是递减的,递减到一时,任何长度都可以被一整除。所以,递归次数不是无限的)。
该算法不需要额外的消耗存储空间,同时时间复杂度为O(n/m+m)
代码如下:(已经过验证)
1 /** 2 递归位移法: 3 leftORright: 判断左移还是右移 4 **/ 5 6 void rotation2(char *stringword, int strlength, int rotlength, bool leftORright ) 7 { 8 int i; 9 char *fpointer, *spointer; 10 int num; 11 12 if(leftORright==true) //左移操作 13 { 14 num=strlength-strlength%rotlength; 15 fpointer=stringword; 16 spointer=stringword+rotlength; 17 charfor(i=0;i<num-rotlength;i++) 18 { 19 20 temp=*(fpointer+i); 21 *(fpointer+i)=*(spointer+i); 22 *(spointer+i)=temp; 23 } 24 leftORright=falseif(strlength%rotlength!=0) 25 { 26 rotation2(stringword+i,rotlength+strlength%rotlength,strlength%rotlength,leftORright); 27 } 28 } 29 else 30 { 31 num=strlength-strlength%rotlength; 32 fpointer=stringword+strlength-1; 33 spointer=fpointer-rotlength; 34 char temp; 35 for(i=0;i<num-rotlength;i++) 36 { 37 temp=*(fpointer-i); 38 *(fpointer-i)=*(spointer-i); 39 *(spointer-i)=temp; 40 } 41 leftORright=true; 42 if(strlength%rotlength!=0) 43 { 44 rotation2(stringword,rotlength+strlength%rotlength,strlength%rotlength,leftORright); 45 } 46 } 47 48 }
3.三步翻转法
基本思想: (X^TY^T)^T=YX
可以把X看成要左转部分,把Y看成原字符串剩余部分,对其分别进行翻转后得到的YX即左转的结果。
该思想可以通过如图3-1展示的手摇法证明:
图3-1. 图片来源:http://blog.csdn.net/v_july_v/article/details/6322882
该算法不需要额外的存储空间,而且时间复杂度为O(n).
代码如下:(已经过验证)
1 //对字符串进行翻转操作,使用双指针 2 void turnover(char *tstring,int start, int end) 3 { 4 5 char temp; 6 7 while(start!=end && start<end) 8 { 9 temp=*(tstring+start); 10 *(tstring+start)=*(tstring+end); 11 *(tstring+end)=temp; 12 start++; 13 end--; 14 } 15 } 16 //得翻转函数的三次调用 17 void rotation3(char *string, int strlength,int rotlength) 18 { 19 turnover(string,0,rotlength-1); 20 turnover(string,rotlength,strlength-1); 21 turnover(string,0,strlength-1); 22 }
算法二和算法三的实现参考了v_JULY_v在CSDN博客上发表的《程序员编程艺术:第一章、左旋转字符串》思想,该博文中还介绍另外两种算法,
博文链接:http://blog.csdn.net/v_july_v/article/details/6322882