字符串循环移位
首先,先看看如何对数字进行循环移位
C语言中没有提供循环移位的操作符,但可以通过简洁的方式实现循环移位,主要使用移位操作来实现。
设一个操作数x有s位则循环左移n位的操作为:
(x << n) | (x >> (s - n));
同理右移n位位:
(x >> n) | (x << (s - n));
实际编程中可以用宏定义实现循环移位:
#define ROTATE_LEFT(x, s, n) ((x) << (n)) | ((x) >> ((s) - (n)))
#define ROTATE_RIGHT(x, s, n) ((x) >> (n)) | ((x) << ((s) - (n)))
下面是一个小的测试程序:
#define ROTATE_LEFT(x, s, n) ((x) << (n)) | ((x) >> ((s) - (n))) #define ROTATE_RIGHT(x, s, n) ((x) >> (n)) | ((x) << ((s) - (n))) int main() { unsigned int a=0x12345678; unsigned b=ROTATE_LEFT(a, 8 * sizeof(int), 4); unsigned c=ROTATE_RIGHT(a, 8 * sizeof(int), 8); printf("original :%08x\n",a); printf("rot_left :%08x\n",b); printf("rot_right:%08x\n",c); return 0; }
输出结果如下:
下面我就要讨论一下怎么实现字符串的移位。
问题,给你一个字符串,要求循环左移n位
比如对"abcdefg" 循环左移2位,我们要得到"cdefgab"
附加条件,不能使用连续辅助空间(包括动态分配),只能使用若干单个变量(即O(1)空间)
附加条件,不能使用连续辅助空间(包括动态分配),只能使用若干单个变量(即O(1)空间)
如果,不限制空间的使用,我们可以按上面数字循环移位的方式来实现,具体如下:
循环左移 | 循环右移 |
void left_shift(char* str,int n) { int len=strlen(str); char *arr=(char*)malloc(sizeof(char)*len+1); printf("%s\n",str); strncpy(arr,str+n,len-n);//将[n,len)区间的字符左移n个位置 printf("%s\n",arr); strncpy(arr+len-n,str,n);//将[0,n)区间的字符右移len-n个位置 cout<<"arr="<<arr<<endl; strncpy(str,arr,len);//将移动好的字符复制回原字符串 free(arr); } |
void right_shift(char* str,int n) { int len=strlen(str); char *arr=(char*)malloc(sizeof(char)*len+1); printf("%s\n",str); strncpy(arr,str+len-n,n);//将[0,len-n)区间的字符右移n个位置 printf("%s\n",arr); strncpy(arr+n,str,len-n);//将[n,len)区间的字符左移len-n个位置 printf("%s\n",arr); strncpy(str,arr,len);//将移动好的字符复制回原字符串 free(arr); }
|
现在考虑需要额外空间的情况。
我们知道,反转一个字符串操作("abcd"变"dcba"),是不需要额外数组辅助的,只要头尾数据交换就可以了。
这儿我们利用多次反转字符串来进行字符串的循环移动:
代码如下:
代码如下:
#include <stdio.h> #include <string.h> //反转字符串,把st与ed所指向的中间的内容反转(包含st不包含ed) void str_rev(char* st, char *ed) { for (--ed; st < ed; ++st, --ed) { char c; c = *st; *st = *ed; *ed = c; } } //用三反转等效左移字符串(st与ed之间,包含st不包含ed的内容) char* str_shl(char* st, char* ed, int n) { str_rev(st, &st[n]); printf("第1段反转:%s\n",st); str_rev( &st[n], ed); printf("第2段反转:%s\n",st); str_rev(st, ed); printf("整体反转 :%s\n",st); return st; } char* str_shr(char* st, char* ed, int n) { str_rev(st, ed); printf("整体反转 :%s\n",st); str_rev(st, &st[n]); printf("第1段反转:%s\n",st); str_rev( &st[n], ed); printf("第2段反转:%s\n",st); return st; } int main() { char str[] = "abcdefghijklmnopqrstuvwxyz"; char str2[] = "abcdefghijklmnopqrstuvwxyz"; puts( str_shl(str, str + strlen(str), 6) ); puts( str_shr(str2, str2 + strlen(str2), 6) ); return 0; }
结果如下:
注意:两段分别反转,最后再整体反转,就实现了循环左移(如果先整体再两部分,就是循环右移)