左旋字符串及其进阶
Posted 大家好我叫张同学
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了左旋字符串及其进阶相关的知识,希望对你有一定的参考价值。
文章目录
左旋字符串
题目内容:
实现一个函数,可以左旋字符串中的k个字符。
例如:
ABCD左旋一个字符得到BCDA
ABCD左旋两个字符得到CDAB
理解题意并画出相应的图解:
思考:首先对于字符串,我们应该主动的将其完整的内容补全,字符串是以‘\\0’为结束标志,虽然字符串’'ABCD"的字符内容为A,B,C,D,但是其存储的时候,实际上存的是A,B,C,D,\\0。
方法一:创建临时数组法
从我们理解的图解中我们可以观察到,左旋k个字符,实际上就是将前k个字符以类似于平移的方式放到字符的后面位置去,具体过程如下:
左旋一个字符:
左旋两个字符:
因此,我们可以尝试去创建一个临时字符数组,
1.如果左旋k个字符,就将字符串的前k个字符先保存在这个临时的字符数组中
2.然后将字符串剩余的字符移动到首地址处,每个字符向前平移k个单位地址
3.然后将临时数组中的前k个字符内容平移回字符串,这样就完成的左旋k个字符的操作
代码实现:
#include<stdio.h>
#include<string.h>
#include<assert.h>
void levotor1(char* arr1, int k)
{
assert(arr1 != NULL);//断言,防止传入空指针,刷题时这句可以去掉
char arr2[5] = { 0 };
int i = 0;
int len = strlen(arr1);//计算好字符串的长度
assert(k <= len);//断言,防止k大于字符串长度,刷题时这句可以去掉
//1.将前k个字符存放到arr2数组中
for (i = 0; i < k; i++)
{
arr2[i] = arr1[i];
}
//2.将字符串第k个字符后面的字符往前平移k个单位
for (i = k; i < len; i++)
{
arr1[i - k] = arr1[i];
}
//3.将临时数组中的内容平移回字符串
for (i = len - k; i < len; i++)
{
arr1[i] = arr2[i - (len - k)];
}
}
int main()
{
char arr1[] = "ABCD";
int k = 0;
scanf("%d", &k);
levotor1(arr1, k);//左旋字符函数
printf("%s\\n", arr1);
return 0;
}
结果展示:
创建临时数组虽然完成了左旋k个字符的操作,但是这种方式是有局限性的,主要体现在:
1、如果字符串的长度发生变化,那么实现创建的临时数组需要给它多大的空间呢?如果给一个定值,比如代码中的5,又或者10、50、100?临时数组的空间给大了,容易造成空间的浪费,空间给小了又无法实现我们的左旋k个字符操作。(有的同学也许会说:“可以用开辟动态数组的方式,比较数组的大小和字符串长度,如果数组大小不够,就用realloc扩大动态数组的空间”。这种方式虽然行得通,但是比较麻烦)
2、如果需要进行左旋的字符串长度很大,那么为了满足左旋操作的需求,临时空间也需要很大,这种空间需求对程序和栈区空间来说压力也挺大的。
为了解决方法一存在的弊端,我们可以尝试方法二:
方法二:临时变量k次法
将左旋n个字符中的n = 1, n = 2, n = 3,观察其过程:
左旋一个字符:
再左旋一个字符:
可以发现:左旋k个字符相当于将左旋一个字符的操作重复了k遍,按照这种思路:
1.实现左旋一个字符的操作
2.利用循环重复
代码实现:
#include<stdio.h>
#include<string.h>
#include<assert.h>
//方法二:临时变量k次法
void levotor2(char* arr1, int k)
{
assert(arr1 != NULL);//断言,防止传入空指针,刷题时这句可以去掉
int i = 0;
int j = 0;
int len = strlen(arr1);
assert(k <= len);//断言,防止k大于字符串长度,刷题时这句可以去掉
//2.重复k遍
for (i = 0; i < k; i++)
{
char tmp = arr1[0];//创建临时变量存放取出来的字符
//1.实现左旋一次的操作
for (j = 0; j < len - 1; j++)
{
arr1[j] = arr1[j + 1];
}
arr1[len - 1] = tmp;
}
}
int main()
{
char arr1[] = "ABCD";
int k = 0;
scanf("%d", &k);
//levotor1(arr1,k);//左旋字符函数:方法一
levotor2(arr1, k);//左旋字符函数:方法二
printf("%s\\n", arr1);
return 0;
}
结果展示:
方法二这种方式相较于方法一而言确实有一定的提升,尤其是再空间利用率这一块(空间复杂度更低),但实际上再运行次数更多(时间复杂度更高),使用两层循环,如果有一个字符串长度为n,左旋n个字符,那么实际运行次数为n ^ 2次,时间复杂度为O(n ^ 2),如果n很大,那么n ^ 2增长后的结果更大,运算时间将变得很长。
所以方法二与方法一相比较,前者是在拿时间换空间,后者是拿空间换时间。
那么有没有一种方法既可以保证空间的有效利用,也能保证运行的时间效率也很高呢?
答案当然是有!
这也就是我将要介绍的方法三!
方法三:三步翻转法
三步翻转法,顾名思义,就是经过翻转三次即可得到左旋k次的结果,为了更好的理解三步翻转法,我们来看下面的具体过程:
以左旋2个字符为例子:
代码实现:
#include<stdio.h>
#include<string.h>
#include<assert.h>
//方法三:三步翻转法
//翻转函数
void reverse(char* start, char* end)//将start和end之间的字符进行翻转
{
while (start < end)
{
char tmp = *start;
*start = *end;
*end = tmp;
start++;
end--;
}
}
void levotor3(char* arr1, int k)
{
assert(arr1 != NULL);//断言,防止传入空指针,刷题时这句可以去掉
int len = strlen(arr1);
assert(k <= len);//断言,防止k大于字符串长度,刷题时这句可以去掉
reverse(arr1, arr1 + k - 1);//1.将前k个字符进行翻转
reverse(arr1 + k, arr1 + len - 1);//2.将第k+1到第len个字符翻转
reverse(arr1, arr1 + len - 1);//3.整体翻转
}
int main()
{
char arr1[] = "ABCD";
int k = 0;
scanf("%d", &k);
//levotor1(arr1,k);//左旋字符函数:方法一
//levotor2(arr1,k);//左旋字符函数:方法二
levotor3(arr1, k);//左旋字符函数:方法三
printf("%s\\n", arr1);
return 0;
}
结果展示:
左旋字符串进阶
题目内容:
写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。
例如:
给定s1 = AABCD和s2 = BCDAA,返回1
给定s1 = abcd和s2 = ACBD,返回0。AABCD左旋一个字符得到ABCDA
AABCD左旋两个字符得到BCDAA
AABCD右旋一个字符得到DAABC
思路:要想判断一个字符串是否为另外一个字符串旋转之后的字符串,可以先将另外的字符串旋转的结果都列出来,如果其中有一个字符串和当前的字符串相同,则是旋转之后的结果,否则就不是旋转之后的结果!
注意:判断两个字符串是否相等不能直接用 == ,而是要用strcmp函数,如果strcmp的返回结果为0,则相等,否则就不相等。
方法一:左旋k次比较法
前提:两个字符串长度相等 如果两个字符串长度不相等,那么必定不是旋转之后的结果
1.将字符左旋1个字符的得到的结果跟另一个字符相比较
2.将步骤1重复len次
代码实现:
#include<stdio.h>
#include<string.h>
#include<assert.h>
//方法三:三步翻转法
//翻转函数
void reverse(char* start, char* end)//将start和end之间的字符进行翻转
{
while (start < end)
{
char tmp = *start;
*start = *end;
*end = tmp;
start++;
end--;
}
}
void levotor3(char* arr1, int k)
{
assert(arr1 != NULL);//断言,防止传入空指针,刷题时这句可以去掉
int len = strlen(arr1);
assert(k <= len);//断言,防止k大于字符串长度,刷题时这句可以去掉
reverse(arr1, arr1 + k - 1);//1.将前k个字符进行翻转
reverse(arr1 + k, arr1 + len - 1);//2.将第k+1到第len个字符翻转
reverse(arr1, arr1 + len - 1);//3.整体翻转
}
//方法一:字符串比较法
int Is_levotor1(char* s1, char* s2)
{
assert(s1 && s2);//断言,防止传入空指针,刷题时这句可以去掉
int len1 = strlen(s1);
int len2 = strlen(s2);
if (len1 != len2)
{
return 0;
}
else
{
int i = 0;
for (i = 0; i < len1; i++)//左旋0个字符和左旋len1个字符的结果是一样的
{
levotor3(s1, 1);
int ret = strcmp(s1, s2);
if (ret == 0)
{
return 1;
}
}
}
return 0;
}
int main()
{
char s1[] = "AABCD";
char s2[] = "BCDAA";
int ret = Is_levotor1(s1, s2);//方法一:字符串比较法
if (ret == 1)
{
printf("s2 is a levotorsion-string of s1.\\n ");
}
else
{
printf("s2 is not a levotorsion-string of s1.\\n ");
}
return 0;
}
方法一实现了判断是否为旋转之后的结果,但是这种方式有两个明显的缺点
一是改变了原字符串的内容,虽然我们可以通过创建临时字符数组的方式来避免这种情况的发生,但是临时数组的大小给多少呢?
二是旋转len次,将每次的结果进行比较看起来有点不太聪明的样子。
实际上每次旋转的结果可以用字符串追加后的字串来表示,例如:
字符串AABCD旋转之后的结果为字符串AABCDAABCD的字串,利用这个特点,我们就不用去旋转len次,而是去追加字符串,判断字串即可,这也是方法二的思路。
方法二:查找字串法
判断一个字符串是否为另外一个字符串的字串,使用函数strstr,不是字串返回一个空指针NULL,否则就返回第一个字串的地址。
字符串追加函数是strcat
代码实现:
#include<stdio.h>
#include<string.h>
#include<assert.h>
//方法二:字符串追加判断字串法
int Is_levotor2(char* s1, char* s2)
{
assert(s1 && s2);//断言,防止传入空指针,刷题时这句可以去掉
int len1 = strlen(s1);
int len2 = strlen(s2);
if (len1 != len2)
{
return 0;
}
char* s[20] = { 0 };
strcat(s, s1);
strcat(s, s1);//s追加两次,相当于s1自己追加自己
if (strstr(s, s2) == NULL)
{
return 0;
}
return 1;
}
int main()
{
char s1[] = "AABCD";
char s2[] = "BCDAA";
//int ret = Is_levotor1(s1, s2);//方法一:字符串比较法
int ret = Is_levotor2(s1, s2);//方法二:字符串追加判断字串法
if (ret == 1)
{
printf("s2 is a levotorsion-string of s1.\\n ");
}
else
{
printf("s2 is not a levotorsion-string of s1.\\n ");
}
return 0;
}
(求点赞,收藏+评论,如果你能关注我,就是对我最大的鼓励!)
完整代码一:
#include<stdio.h>
#include<string.h>
#include<assert.h>
//方法一:临时数组法
void levotor1(char* arr1,int k)
{
assert(arr1 != NULL);//断言,防止传入空指针,刷题时这句可以去掉
char arr2[5] = { 0 };
int i = 0;
int len = strlen(arr1);//计算好字符串的长度
assert(k <= len);//断言,防止k大于字符串长度,刷题时这句可以去掉
//1.将前k个字符存放到arr2数组中
for (i = 0; i < k; i++)
{
arr2[i] = arr1[i];
}
//2.将字符串第k个字符后面的字符往前平移k个单位
for (i = k; i < len; i++)
{
arr1[i-k] = arr1[i];
}
//3.将临时数组中的内容平移回字符串
for (i = len - k; i < len; i++)
{
arr1[i] = arr2[i-(len-k)];
}
}
//方法二:临时变量k次法
void levotor2(char* arr1, int k)
{
assert(arr1 != NULL);//断言,防止传入空指针,刷题时这句可以去掉
int i = 0;
int j = 0;
int len = strlen(arr1);
assert(k <= len);//断言,防止k大于字符串长度,刷题时这句可以去掉
//2.重复k遍
for (i = 0; i < k; i++)
{
char tmp = arr1[0];//创建临时变量存放取出来的字符
//1.实现左旋一次的操作
for (j = 0; j < len - 1; j++)
{
arr1[j] = arr1[j + 1];
}
arr1[len - 1] = tmp;
}
}
//方法三:三步翻转法
//翻转函数
void reverse(char* start, char* end)//将start和end之间的字符进行翻转
{
while (start < end)
{
char tmp = *start;
*start = *end;
*end = tmp;
start++;
end--;
}
}
void levotor3(char* arr1, int k)
{
assert(arr1 != NULL);//断言,防止传入空指针,刷题时这句可以去掉
int len = strlen(arr1);
assert(k <= len);//断言,防止k大于字符串长度,刷题时这句可以去掉
reverse(arr1, arr1 + k - 1);//1.将前k个字符进行翻转
reverse(arr1 + k, arr1 + len - 1);//2.将第k+1到第len个字符翻转
reverse(arr1, arr1 + len - 1);//3.整体翻转
}
int main()
{
char arr1[] = "ABCD";
int k = 0;
scanf("%d", &k);
//levotor1(arr1,k);//左旋字符函数:方法一
//levotor2(arr1,k);//左旋字符函数:方法二
levotor3(arr1, k);//左旋字符函数:方法三
printf("%s\\n", arr1);
return 0;
}
完整代码二:
#include<stdio.h>
#include<string.h>
#include<assert.h>
//方法三:三步翻转法
//翻转函数
void reverse(char* start, char* end)//将start和end之间的字符进行翻转
{
while (start < end)
{
char tmp = *start;
*start = *end;
*end = tmp;
start++;
end--;
}
}
void levoto以上是关于左旋字符串及其进阶的主要内容,如果未能解决你的问题,请参考以下文章