左旋转字符串
来源:互联网 发布:无限流量破解软件 编辑:程序博客网 时间:2024/04/30 03:46
题目:字符串的左旋转操作:把字符串前面的若干字符移到字符串的后面。例如:字符串abcdefg左旋转2位得到cdefgab。请实现左旋转字符串函数,要求对于长度为n的字符串,时间复杂度为O(n),辅助内存为O(1)。
分析1:看到这道题我们最直观的思路就是:如果要把长度为n的字符串左移k位,那么我们可以动态分配长度为k的临时数组,存储前面k个字符,然后将后面的字符逐一向前移动k位,然后将临时数组中的字符拷贝到后面k位。但是这种方法使用的k个额外位置产生了过大的存储空间的消耗。
分析2:我们也可以定义一个子函数,该函数的功能是将一个字符串左旋转1位(其时间正比于n),然后调用该函数k次。但是该方法又产生了过多的时间消耗。
分析3:要在题目限定的范围内解决该问题,我们有一个著名的“杂技算法”:移动S[0]到临时变量t,然后移动S[K]到S[0];移动S[2K]到S[K],依此类推(将S中的所有下标对n取模),直到返回到取S[0]中的元素,此时改为从t取值然终止过程。当n为12,k为3时,元素按如下顺序移动(如下图,画得太丑了,莫怪)。如果该过程没有移动全部的元素,则从S[1]开始再次移动,直到所有的元素都已经移动位置。
根据上面的思路,我们可以写出如下的代码:
1 #include<iostream> 2 #include<string> 3 using namespace std; 4 5 //求两个整数i,j的最大公约数 6 int gcd(int i,int j) 7 { 8 while(i != j) 9 {10 if(i > j)11 i -= j;12 else13 j -= i;14 }15 return i;16 }17 18 19 //左旋转字符串函数,将pStr字符串向左旋转k位,20 //first指向pStr[i],second指向pStr[2i],以此类推21 char* LeftRotateString(char* pStr,int k)22 {23 if(pStr == NULL || k < 1)24 return 0;25 26 int n = strlen(pStr);27 28 //共进行i趟循环,i为n,k的最大公约数29 for(int i = 0;i < gcd(n,k);++i)30 {31 char temp = pStr[i];32 int first = i;33 while(1)34 {35 int second = (first + k) % n;36 37 if(second == i)38 break;39 40 pStr[first] = pStr[second];41 first = second;42 }43 44 //将临时变量中的字符存至每一趟的循环的最后一个字符中45 pStr[first] = temp;46 }47 return pStr;48 }49 50 int main()51 {52 string demo("abcdefgh");53 cout<<demo<<endl;54 55 LeftRotateString(&demo[0],3);56 57 cout<<demo<<endl;58 return 0;59 }
运行结果如下:
效率分析:很显然这个算法的空间复杂度是O(1),看似是for循环中嵌套了while循环,然而仔细想想不难发现,这个算法实际上只遍历了一个数组,只不过采取的遍历方式是“跨越式”访问的,所以它的时间复杂度是O(n),满足题目的要求,是一个比较好的算法了。
分析4:我们可以将该问题看成是数组ab转换成ba,同时假定我们有一个子函数可以将数组中的所有元素求逆序。显然有(a^r)^r=a,即对一个数组a求逆再求逆等于它本身。所以我们从ab开始,首先对a求逆,得到a^r,然后对b求逆得到b^r。最后整体求逆,得到(a^r*b^r)^r=ba。这正是我们要的结果!
于是我们有如下代码:
1 #include<string> 2 #include<iostream> 3 using namespace std; 4 5 //对一个数组中的所有元素求逆,即把abcde变为edcba 6 void ReverseString(char *pStart,char *pEnd) 7 { 8 if(pStart != NULL && pEnd != NULL) 9 {10 while(pStart <= pEnd)11 {12 char temp = *pStart;13 *pStart = *pEnd;14 *pEnd = temp;15 16 ++pStart;17 --pEnd;18 19 }20 }21 }22 23 24 //把字符串pStr左旋转k位25 char* LeftRotateString(char* pStr, int k)26 {27 if(pStr != NULL && k > 0)28 {29 int n = strlen(pStr);30 if(n > 0 && k < n)31 {32 char* pFirstStart = pStr;33 char* pFirstEnd = pStr+k-1;34 char* pSecondStart = pStr+k;35 char* pSecondEnd = pStr+n-1;36 37 //求第一部分字符串的逆,即求a^r38 ReverseString(pFirstStart,pFirstEnd);39 //求第二部分字符串的逆, 即求b^r40 ReverseString(pSecondStart,pSecondEnd);41 //求前两部分整体的逆, 即求(a^r*b^r)^r42 ReverseString(pFirstStart,pSecondEnd);43 }44 }45 46 return pStr;47 }48 49 50 int main()51 {52 string demo("abcdefgh");53 cout<<demo<<endl;54 55 LeftRotateString(&demo[0],3);56 57 cout<<demo<<endl;58 return 0;59 60 }
运行结果如下:
效率分析:“翻转代码在时间和空间上效率都很高,而且代码非常的简短,很难出错”(出自《编程珠玑》)。实际上由于要进行ab的求逆运算,对a,b分别求逆的时候相当于遍历一遍数组,而对整体求逆的时候又要遍历一边数组,这样整体看来对于长度为n的字符串需要遍历两遍,因此时间复杂度为O(n)。但是翻转代码不需要额外分配新的内存,一个额外的空间都不需要。
参考文献:
【1】Jon Bentley. 《编程珠玑(第二版)》. 人民邮电出版社,2008 p13-p15
【2】程序员面试题精选100题:http://zhedahht.blog.163.com/blog/static/2541117420073993725873/
注:
1)本博客所有的代码环境编译均为win7+VC6。所有代码均经过博主上机调试。
2)博主python27对本博客文章享有版权,网络转载请注明出处http://www.cnblogs.com/python27/。对解题思路有任何建议,欢迎在评论中告知。
- 左旋转字符串
- 左旋转字符串
- 左旋转字符串
- 左旋转字符串
- 左旋转字符串
- 26.左旋转字符串
- 第一章、左旋转字符串
- 字符串的左旋转
- 第一章、左旋转字符串
- 第一章、左旋转字符串
- 左旋转字符串
- 左旋转字符串
- 26、左旋转字符串
- 左旋转字符串
- 左旋转字符串
- 左旋转字符串
- 左旋转字符串
- 左旋转字符串
- 判断扑克牌中的顺子
- 关于二叉树的遍历
- 你若安好
- 自定义Qt窗口部件
- 有名管道(FIFO)的用法
- 左旋转字符串
- 类模板static成员的使用
- SystemServer->SystemThread->run
- Install Openldap details
- 你若安好
- Android PopupWindows
- 常用色彩空间分类总结
- Spring Batch 2.1.8 中文文档(四)
- innodb buf ratio 各项指标计算方法