有一道关于字符串旋转的题比较有意思,拿来和大家分享一下,题目是这样的:
实现一个函数,可以左旋字符串中的k个字符。
例如:AABCD左旋一个字符得到ABCDA,AABCD左旋两个字符得到BCDAA现在就来讲讲这个三步旋转法是怎么实现左旋的,先假设 k 的值为 1 :
1、把原字符串整体逆序----------------------->>>DCBAA;
2、把左半部分的字符串(DCBA)逆序--->>>ABCDA;
3、把右半部分的字符串(A)逆序---------->>>ABCDA
当k为其它值的时候也是使用的哦!
按照我以前的套路,先把整个程序框架建起来:
头文件我们也先摆在这里,一会要用到,接下来是字符串左旋函数的具体实现:#include <stdio.h> #include <string.h> #include <assert.h> int main() { char arr[] = "aabcd"; printf("%s\n", left_move(arr, 2)); return 0; }
char * left_move(char * src, int k) { int i = 0; int len = strlen(src); char tmp = 0; assert(src); //保证src有效性 for (i = 0; i < len / 2; i++) //整体逆序 { tmp = *(src + i); *(src + i) = *(src + len - 1 - i); *(src + len - 1 - i) = tmp; } for (i = 0; i < (len - k) / 2; i++) //左串逆序 { tmp = *(src + i); *(src + i) = *(src + len - k - 1 - i); *(src + len - k - 1 - i) = tmp; } for (i = 0; i < k / 2; i++) //右串逆序 { tmp = *(src + len - k + i); *(src + len - k + i) = *(src + len - 1 - i); *(src + len - 1 - i) = tmp; } return src; }
也就是连续三次字符串的逆序,是不是觉得很简单呢!
这个程序还可以优化一下,比如把其中多次用到的逆序操作封装成一个函数,这样,整个程序看起来可以更加的简洁! 这里我偷个懒就不再改了。
这道题还有一个II版本,也让我们来分析一下:
判断一个字符串是否为另外一个字符串旋转之后的字符串。
例如:给定s1 = AABCD和s2 = BCDAA,返回1,给定s1=abcd和s2=ACBD,返回0.
由于我们已将有了左旋的函数了,所以这道题也就不难了。想一想,其实只要我们每次旋转一个字符,然后比较一下,这样一圈下来,结果不就出来了吗?
但要注意的是,字符串的 旋转可不仅有左旋,还有右旋!那么对于右旋的情况怎么办呢?要再实现一个右旋函数么?
其实仔细想想,完全没有这个必要:我们只需先依次左旋s1再与s2进行比较,发现不相等,就接着左旋s2然后与s1进行比较就好了,脑筋转不过来弯不妨画个图看看吧
现在就来改一改上面的程序:
#include <stdio.h> #include <string.h> #include <assert.h> /* * 实现一个函数,可以左旋字符串中的k个字符。 * AABCD左旋一个字符得到ABCDA * AABCD左旋两个字符得到BCDAA */ char * left_move(char * src, int k) { int i = 0; int len = strlen(src); char tmp = 0; assert(src); //保证src有效性 for (i = 0; i < len / 2; i++) //整体逆序 { tmp = *(src + i); *(src + i) = *(src + len - 1 - i); *(src + len - 1 - i) = tmp; } for (i = 0; i < (len - k) / 2; i++) //左串逆序 { tmp = *(src + i); *(src + i) = *(src + len - k - 1 - i); *(src + len - k - 1 - i) = tmp; } for (i = 0; i < k / 2; i++) //右串逆序 { tmp = *(src + len - k + i); *(src + len - k + i) = *(src + len - 1 - i); *(src + len - 1 - i) = tmp; } return src; } /* * 判断一个字符串是否为另外一个字符串旋转之后的字符串。 * 例如:给定s1 = AABCD和s2 = BCDAA,返回1,给定s1=abcd和s2=ACBD,返回0. */ int is_string_move(char * left, char * right) { int len1 = strlen(left); int len2 = strlen(right); int i = 0; assert(left); assert(right); for (i = 0; i < len1; i++) //依次左移left串与right比较,循环len次后,刚好回到初始位置 { if (0 == strcmp(left_move(left, 1), right)) return 1; } for (i = 0; i < len1; i++) //依次左移right串与left比较,相当于右移left串与right比较 { if (0 == strcmp(left_move(left, 1), right)) return 1; } return 0; } int main() { char arr[] = "aabcd"; char arr2[] = "abcde"; if (is_string_move(arr2, arr)) printf("yes\n"); else printf("no\n"); return 0; }
这样肯定是可以的,但是这样的效率是不怎么高的!所以我就又想了想,其实还可以这样做:
s1 = AABCD和s2 = BCDAA,我们把s1变为AABCDAABCD,这样,我们只需在其中查找是否包含s2这个子串就好了!因为修改后的s1包含了所有旋转后的可能的情况。
新实现的代码如下:
仔细看看上面这段代码,看起来似乎可以,然而我们编译运行一下,发现崩溃了!为什么?int is_string_move2(char * left, char * right) { int len_left = strlen(left); int len_right = strlen(right); assert(left); assert(right); if (len_left != len_right) //长度不等,必不存在 return 0; strcat(left, left); if (strstr(left, right) != NULL) return 1; else return 0; }
原来是strcat()这个函数出了问题,因为strcat是把str拷贝到str的末尾,所以会出现永远拷不完的情况,开始时str要移到str的末尾
然后把str字符挨个拷到str末尾,变成这样:
发现str永远拷不完了!
所以我们改用strncat()函数,是一个相对来说较安全的一个函数,每次调用可以指定拷多少个字符:
int is_string_move2(char * left, char * right) { int len_left = strlen(left); int len_right = strlen(right); assert(left); assert(right); if (len_left != len_right) //长度不等,必不存在 return 0; strncat(left, left, len_left, len_left); if (strstr(left, right) != NULL) return 1; else return 0; }
这样程序就没有问题了!
字符串旋转问题
最新推荐文章于 2022-09-09 09:42:53 发布