- 论坛徽章:
- 36
|
本帖最后由 cokeboL 于 2018-07-28 18:46 编辑
- #include <cstdio>
- #include <string.h>
- int timeCount = 0;
- //len为需要转换的总长度,n是旋转的长度
- void str_turn_left(char *src, int len, int n)
- {
- int i, end = len - 1;
- char tmp;
- n = n % len;
- if(0 == (len * n))
- return;
- while(end >= n - 1){
- for(i = n-1; i >= 0; i--, end--){
- timeCount++; //时间复杂度计数
- tmp = src[end];
- src[end] = src[i];
- src[i] = tmp;
- }
- }
- if((len % n) > 1)
- str_turn_left(src, len % n, n%(len % n));
- }
- int main()
- {
- int i;
- char a[15][20] =
- {
- "a",
- "ab",
- "abc",
- "abcd",
- "abcde",
- "abcdef",
- "abcdefg",
- "abcdefgh",
- "abcdefghi",
- "abcdefghij",
- "abcdefghijk",
- "abcdefghijkl",
- "abcdefghijklm",
- "abcdefghijklmn",
- "abcdefghijklmno",
- };
- for(i = 0; i < sizeof(a)/sizeof(a[0]); i++){
- printf("%s ", a[i]);
- str_turn_left(a[i],strlen(a[i]),13);
- printf("-> %s\n", a[i]);
- printf("timeCount: %d\n", timeCount);
- timeCount = 0;
- }
- return 0;
- }
复制代码
解释下算法:
把字符串按照旋转的长度n分成几段,字符串头的前n个记为1,后面往前数依次为-1、-2,此图举例,到-3段时与1段有交叉;
算法依次按后续调换1段和-1段、1段和-2段、1段和-3段。。。类推;
每次调换-m段时-m段被放到1段的位置,下次和-(m-1)段调换,循环之后从后面数的段依次向前移动了n个字节,并且第一段在第一次调换时已经放在字符串尾部;
最后一次调换,就是第一段和图中-3段调换有交叉或者临界的情况,类似于memmove函数,由于是从后往前调换,可知交叉部分最后被放到了头部,交叉部分之前的部分被调换到了之后的位置,比如 "abcde",用后3个字符“cde”调换前3个字符“abc”之后,变成了“edabc”,这时需要再把'e'和'd'调换,就是旋转交叉部分前面的字符串;
通用的公式: 再次旋转的长度就是1段的长度减去交叉部分的长度,即strlen % n,旋转的位数就是交叉部分的长度,即 n % (len % n),当len % n <= 1时不交叉或者为单字符,不需要旋转,详见代码
总的来讲,用一个字节进行段的替换,使字符串开头的段成了临时的buf用来存储下次需要调换的字节,这样用1个字节+自身空间实现,节省了单独开辟buf的内存,使空间复杂度为O(1) go,如果不可以,那就随意吧,如果不是cpu消耗型的,性能问题不大,如果用5.1的话可以把luajit搞上比lua快很多
|
|