题目:实现一个函数,能够左旋字符串中的k个字符。举个例子:对于字符串“abcdef”左旋1个字符结果为“bcdefa”,若左旋3个字符结果为“defabc”。
解法一:一般方法
先将字符串首字符保存起来,然后将后面剩余的字符全部往前挪一位,最后将保存的首字符覆盖掉最后一位字符。如下图所示:。这样不就做到了字符串左旋一个字符,自然左旋n个字符就只要执行n次就可以了。
代码如下:
#include<stdio.h>
#include<string.h>
void left_move(char arr[], int n)
{
int sz = strlen(arr);
n %= sz;//去掉重复的循环
while (n--)
{
//1.保存首字符
char tmp = arr[0];
//2.将剩余字符往前挪移一位
int i = 0;
for (i = 1; i < sz; i++)
{
arr[i - 1] = arr[i];
}
//3.将保存的首字符放到最后一位
arr[sz - 1] = tmp;
}
}
int main()
{
char arr[] = "abcdef";
int n = 0;
printf("请输入旋转字符个数:\n");
scanf("%d", &n);
left_move(arr, n);
printf("%s\n", arr);
return 0;
}
解法二:翻转法
翻转法的用处就是能将字符串拆分的各个部分,从原来的顺序排放变成逆序排放。假设我要将字符串“abcdef”左旋2次,可以先将它拆分成“ab”和“cdef”两个部分,然后分别对其进行逆序操作得到“bafedc”,最后将整个字符串逆序就能得到左旋2个字符后的字符串“cdefab”,这就是翻转法。如下图所示:
代码如下:
#include<stdio.h>
#include<string.h>
void reverse(char* left, char* right)
{
while (left < right)
{
char tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
void left_move(char arr[], int n)
{
int sz = strlen(arr);
n %= sz;//去掉重复的循环
//逆序将要被旋转的字符
reverse(arr, arr + n - 1);
//逆序剩余的字符
reverse(arr + n, arr + sz - 1);
//逆序整个字符串
reverse(arr, arr + sz - 1);
}
int main()
{
char arr[] = "abcdef";
int n = 0;
printf("请输入旋转字符个数:\n");
scanf("%d", &n);
left_move(arr, n);
printf("%s\n", arr);
return 0;
}
解法一的算法思想较为简单,但是实际执行的时候较为繁琐,解法二则略优于解法一。
题目:实现一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。例如:给定s1 =AABCD和s2 = BCDAA,返回1。给定s1=abcd和s2=ACBD,返回0。
首先,我们要知道判断一个字符串知否为另一个字符串旋转之后的字符串,完全没有必要既判断是否为左旋又判断是否为右旋。因为左旋得到的结果完全可以通过右旋得到,故两种旋转方式只需选一种来判断就行。
解法一:
我们可以通过字符串每一次左旋得到的结果来与之比较,只要其中有一次能得到两字符串相等,那么就代表可以由另一个字符串旋转的来,反之则不能由另一个字符串得来。代码如下:
#include<stdio.h>
#include<string.h>
int is_left_move(const char* str1, char* str2)
{
int sz = strlen(str2);
if (sz != strlen(str1))
return 0;
int n = sz;
while (n--)
{
char tmp = str2[0];
int i = 0;
for (i = 1; i < sz ; i++)
{
str2[i - 1] = str2[i];
}
str2[sz - 1] = tmp;
if (strcmp(str1, str2) == 0)
return 1;
}
return 0;
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "bcdefa";
int flag = is_left_move(arr1, arr2);
if (flag == 1)
{
printf("可以通过旋转得来\n");
}
else
{
printf("不可以通过旋转得来\n");
}
return 0;
}
解法二:
解法一的执行效率还是太低了,判断是否由另一个字符串旋转得来,最坏的情况下需要将整个字符串的所有情况都遍历一遍,所以不推荐使用。在这里我讲一下另一种解法,如果一个字符串在自己的后面追加一个自己,这样我们会发现关于字符串的所有的旋转的情况都存在于这个追加后的字符串中了。举个例子:字符串“abcdef”追加后“abcdefabcdef”,那么“bcdefa”、“cdefab”、“defabc”……都是字符串“abcdefabcdef”的子字符串,都是字符串“abcdef”旋转之后的情况。所以在这要判断一个字符串是否由另一个字符串旋转得来,我们只需要判断是否为追加后字符串的子字符串就可以了。代码如下:
#include<stdio.h>
#include<string.h>
int is_left_move(const char* str1, char* str2)
{
int sz = strlen(str2);
if (sz != strlen(str1))
return 0;
//strncat是长度受限制的字符串连接函数
strncat(str2, str2, sz);//实现自己追加自己
//strstr是用来判断是否为子字符串的函数
char* ret = strstr(str2, str1);
//strstr若没有找到子字符串会返回NULL,否则返回该子字符串的起始地址
if (ret == NULL)
return 0;
else
return 1;
}
int main()
{
char arr1[] = "abcdef";
char arr2[20] = "aadefa";
int flag = is_left_move(arr1, arr2);
if (flag == 1)
{
printf("可以通过旋转得来\n");
}
else
{
printf("不可以通过旋转得来\n");
}
return 0;
}
注意:判断两个字符串长度是否先等这一步必不可少,若没有代码会存在一个漏洞,字符串“abc”也是“abcdefabcdef”的子字符串,但其并不能由“abcdef”旋转得来。
这份博客👍如果对你有帮助,给博主一个免费的点赞以示鼓励欢迎各位🔎点赞👍评论收藏⭐️,谢谢!!!
如果有什么疑问或不同的见解,欢迎评论区留言欧👀。