字符旋转及逆序输出问题

简介: 写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。例如:给定s1 =AABCD和s2 = BCDAA,返回1给定s1=abcd和s2=ACBD,返回0.

问题描述

写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。

例如:给定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;
}


相关文章
|
XML 运维 监控
【共读】企业信息安全建设与运维指南(二)
【共读】企业信息安全建设与运维指南(二)
982 0
【共读】企业信息安全建设与运维指南(二)
|
7月前
|
Linux Shell
问题记录:解决Linux登录故障,/etc/passwd配置受损该怎么操作
修复/etc/passwd文件是解决Linux登录故障的重要步骤。通过进入单用户模式、挂载文件系统、恢复或手动修复/etc/passwd文件,可以有效解决该问题。保持定期备份系统配置文件是预防此类问题的最佳实践。
230 5
|
10月前
|
API
手机号段查询城市免费API接口教程
此接口用于根据手机号段查询相关信息,包括城市、运营商、区号等。支持POST和GET请求,需提供用户ID、KEY及手机号段前7位作为参数。返回数据包含状态码、信息提示及详细归属信息。示例请求地址:https://cn.apihz.cn/api/ip/haoduan.php?id=88888888&key=88888888&numbers=1321993。接口免费,建议使用个人ID与KEY以独享调用频次。
|
9月前
|
存储 NoSQL Linux
linux积累-core文件是干啥的
核心文件是Linux系统在程序崩溃时生成的重要调试文件,通过分析核心文件,开发者可以找到程序崩溃的原因并进行调试和修复。本文详细介绍了核心文件的生成、配置、查看和分析方法
554 6
|
11月前
|
测试技术 数据安全/隐私保护
北邮人论坛登录页面测试用例
北邮人论坛登录页面测试用例
198 1
|
运维 监控 安全
系统故障排查与问题解决指南:步步为营,精准定位
【8月更文挑战第16天】系统故障排查与问题解决是一项复杂而艰巨的任务,需要运维人员具备扎实的专业知识、丰富的实践经验以及良好的沟通能力和团队合作精神。通过遵循本文提供的指南,您可以更加高效地应对系统故障挑战,保障系统的稳定运行和业务的持续发展。
|
10月前
|
Prometheus 监控 Java
深入探索:自制Agent监控API接口耗时实践
在微服务架构中,监控API接口的调用耗时对于性能优化至关重要。通过监控接口耗时,我们可以识别性能瓶颈,优化服务响应速度。本文将分享如何自己动手实现一个Agent来统计API接口的调用耗时,提供一种实用的技术解决方案。
342 3
|
11月前
|
前端开发 JavaScript API
自定义React Hooks综合指南
本文介绍了React Hooks及其在组件开发中的作用,重点讲解了自定义Hook的创建和使用方法。通过实例展示了如何创建`useWindowWidth`、`useFetch`和`useForm`等自定义Hook,并分享了使用自定义Hook的最佳实践。文章强调了自定义Hook在提高代码复用性和组件可维护性方面的重要性。
238 0
|
Dubbo 安全 Java
ThreadPoolExecutor线程池参数及其设置规则
ThreadPoolExecutor线程池参数及其设置规则
567 1
HttpURLConnection中请求头中携带Token的使用方法
HttpURLConnection中请求头中携带Token的使用方法
686 2