字符串循环移位

首先,先看看如何对数字进行循环移位

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)空间)
如果,不限制空间的使用,我们可以按上面数字循环移位的方式来实现,具体如下:
循环左移 循环右移
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;  
}  

结果如下:

注意:两段分别反转,最后再整体反转,就实现了循环左移(如果先整体再两部分,就是循环右移)
posted @ 2012-05-20 16:26  Mr.Rico  阅读(2548)  评论(0编辑  收藏  举报