问题描述
写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。
例如:给定s1 =AABCD和s2 = BCDAA,返回1
给定s1=abcd和s2=ACBD,返回0.
其中:
AABCD左旋一个字符得到ABCDA
AABCD左旋两个字符得到BCDAA
AABCD右旋一个字符得到DAABC
在我们正式开始学习字符串旋转之前,我们还需要了解一些简单的内容:strcpy函数、strcmp函数、strncat函数、gets的使用。
~~~~~~~~~~这些内容请看c语言字符函数和字符串函数的定义与使用~~~~~~~~~~
问题分析
首先,我们得先写出一个函数用于翻转我们给的第一个字符串s1,然后再让翻转后的s1与我们提供的第二个字符串s2做最后比较。对于字符翻转函数内部的具体翻转过程如下图:
我们会发现当进行第四次翻转时,字符串就会回到原来的位置,当我们进行第五次翻转时其实就相当于它翻转了一次,所以我们会想到使用 取模运算符(%)让"翻转次数%字符串长度",这样就可以减少系统翻转的次数加快运行效率,原来可能要翻转十五次但是现在只需要三次。
我们还发现,每次翻转后用绿色圈起来的部分就相当于依据反转次数time从原来的字符串中截取前time个字符,然后再拼接到另一个字符串上去了,所以我们想到了将截取获得的字符串和未截取的字符串分为两个部分,最后将截取字符串拼接在未截取字符串后面即可。具体代码如下:
char Change(char* str, int put) //(s1字符串首字符地址,旋转次数) { char tmp[256] = { 0 }; //设定一个用于存放未翻转字符串的临时字符数组 int len = strlen(str); //我们这里假设len=4 ,put=3 int time = put % len; //time = 3 % 4 = 3 strcpy(tmp, str + time); //strcpy(tmp,A+3) ,将指向A的指针+3此时指向的位置就是D了,然后将D放入临时数组tmp中,如果为2那么拼接过去的就是CD了 strncat(tmp, str, time); //strncat(tmp,str,3),将ABCD的前三个字符拼接至tmp后 strcpy(str, tmp); //最后将tmp重新赋值给str,此时str表示的是已经完成翻转的字符串 }
主函数代码
int main() { int i = 0; char str[100] = { '\0' }; //定义两个足够长的字符数组,初始化所有位为 '\0',这是因为strlen函数查找时是遇到 '\0'才会停止的,如果不初始化为 '\0' 可能就会出现越界访问问题 char str2[100] = { '\0' }; int len = strlen(str); //使用strlen函数获取字符串长度 printf("请输入两个字符串:\n"); gets(str); //gets相比于scanf而言可以读取字符串里面的空格 gets(str2); Change(str, 3); if (strcmp(str, str2) == 0) //strcmp函数用于比较两个字符串是否完全相等,这里字符串相等时返回的结果为而非1 { printf("数组一样\n"); } else { printf("数组不一样\n"); } return 0; }
这里面其实还包含了字符串左旋问题:
实现一个函数,可以左旋字符串中的k个字符。
例如:
ABCD左旋一个字符得到BCDA
ABCD左旋两个字符得到CDAB
只需要将最后的比较删除改为输出即可。
~~~~~~~~~~~~~~~~~~~~~~~以上就是该问题的详解~~~~~~~~~~~~~~~~~~~~~~~
小拓展:逆序输出和字符串局部左旋
逆序输出
方法一:
这段代码很简单请自行理解
int main() { char str[10000] = {'\0'}; gets(str); int len = strlen(str); for (int i = len-1; i >= 0; i--) { printf("%c", str[i]); //依据数组下标逐渐减小逐个打印数组 } return 0; }
方法二:
具体解释都在注释中,就不再过多的表述了,理解就好
void Reverse(char* str) { char* left = str; //初始时,left为数组首元素地址 char* right = str + strlen(str)-1; //初始时,right为数组尾元素地址 while(left < right) //如果字符串长度为奇数个,最后left和right会指向同一个数字,而这个数字肯定是位于字符串最中间的位置不需要再交换了,所以只需要left<right就能满足使用 { char temp = *left; //交换此时left和right指针指向地址中存放的数字 *left = *right; *right = temp; ++left; //将left和right指针指向的地址的++和--,往中间靠拢 --right; } } int main() { char str[101] = {0}; while(gets(str)) { Reverse(str); printf("%s\n", str); memset(str, 0, sizeof(str)/sizeof(str[0])); } return 0; }
通过画图对里面的left和right的移动有更直观的理解:
第一次交换后:
++left,--right后:(画图技术有限的😂)
第二次交换后:
当left和right相等时:
这种形式在后续的数据结构的学习中会用到的更多。
字符串局部旋转
具体代码如下:
void Reverse(char* str, int left, int right) //这里与上面的意思差不多 { while (left < right) { char tmp = *(str + left); *(str + left) = *(str + right); *(str + right) = tmp; left++; right--; } } void leftRound(char* str,int k) //这里的k表示需要进行局部翻转的个数 { int len = strlen(str); Reverse(str, 0, k - 1); //前三个字符翻转,翻转结束并保存 Reverse(str, k, len - 1); //在前者基础上后三个字符翻转,翻转后结果自动保存 Reverse(str, 0, len - 1); //将所有的结果都进行翻转 }
每增加一行Rsverse函数结果都会发生相应的变化,依据变化可以逆推得到代码的意思:
最后的主函数就是这样的:
int main() { char str[] = "abcdef"; leftRound(str,2); printf("%s\n", str); return 0; }