[c语言日寄]字符串的左旋与右旋

在这里插入图片描述

【作者主页】siy2333
【专栏介绍】⌈c语言日寄⌋:这是一个专注于C语言刷题的专栏,精选题目,搭配详细题解、拓展算法。从基础语法到复杂算法,题目涉及的知识点全面覆盖,助力你系统提升。无论你是初学者,还是进阶开发者,这里都能满足你的需求!
【食用方法】1.根据题目自行尝试 2.查看基础思路完善题解 3.学习拓展算法
【Gitee链接】资源保存在我的Gitee仓库:https://gitee.com/siy2333/study


文章目录

  • 前言
  • 一、题目引入
  • 二、子功能介绍
    • 1. 左旋操作
      • a. 创建一个临时数组来存储需要移动到末尾的前 n 个字符。
      • b. 将剩余的字符向前移动 n 个位置。
      • c. 将临时数组中的字符复制到字符串的末尾。
      • d. 释放临时数组占用的内存。
    • 2. 右旋操作
  • 三、注意事项
    • 1. 输入验证
    • 2. 内存管理
    • 3. 字符串比较
    • 4. 旋转次数优化
    • 5. 字符串复制
  • 四、题目分析解答
    • 问题分析
    • 代码实现
    • 代码分析
  • 五、拓展应用
  • 总结


前言

在C语言中,字符串操作是一个非常重要的主题,它不仅涉及到基础的字符处理,还涉及到算法设计和数据结构的运用。今天,我们通过一个有趣的题目——判断一个字符串是否为另一个字符串旋转后的字符串,来深入探讨字符串的左旋与右旋操作。这个问题不仅能帮助我们理解字符串的基本操作,还能锻炼我们的算法思维。接下来,我们将从题目引入、子功能介绍、注意事项、题目分析解答以及拓展应用五个方面展开讨论。


一、题目引入

在编程中,字符串的旋转操作是一种常见的问题。有以下题目:

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

例如:

  • 给定s1 =AABCD 和 s2 = BCDAA,返回1
  • 给定s1=abcd 和 s2=ACBD,返回0.

其中:

  • AABCD左旋一个字符得到ABCDA
  • AABCD左旋两个字符得到BCDAA
  • AABCD右旋一个字符得到DAABC

这个问题看似简单,但背后涉及到字符串的拼接、比较以及内存管理等多方面的知识:不仅需要我们理解字符串的基本操作,还需要我们掌握如何高效地实现旋转操作以及如何判断两个字符串是否相等。接下来,我们将通过实现左旋和右旋的子功能来逐步解决这个问题。

二、子功能介绍

为了实现字符串的旋转操作,我们需要设计两个核心子功能:左旋和右旋。这两个功能将帮助我们对字符串进行操作,并为最终的判断提供支持。

1. 左旋操作

左旋操作是指将字符串的前 n 个字符移动到字符串的末尾。

例如,对于字符串 “AABCD”,左旋两个字符后得到 “BCDAA”。

左旋操作的实现需要以下步骤:
我们假设有一个数组a:在这里插入图片描述

a. 创建一个临时数组来存储需要移动到末尾的前 n 个字符。

在这里插入图片描述

b. 将剩余的字符向前移动 n 个位置。

在这里插入图片描述

c. 将临时数组中的字符复制到字符串的末尾。

在这里插入图片描述

d. 释放临时数组占用的内存。

free(str_b);

以下是左旋操作的代码实现:

void left(char* str, const int n) {
    assert(str != NULL);
    int size = (int)strlen(str);
    assert(n > 0);
    assert(n <= size);

    // 创建临时数组
    char* str_b = (char*)malloc(sizeof(char) * n);
    if (str_b == NULL) {
        printf("内存不足");
        return;
    }

    // 另存前n个数
    for (int i = 0; i < n; i++) {
        str_b[i] = str[i];
    }

    // 把数往前推移n个
    for (int i = 0; i < size - n; i++) {
        str[i] = str[i + n];
    }

    // 将n赋值到目前数组后
    for (int i = 0; i < n; i++) {
        str[size - n + i] = str_b[i];
    }

    // 释放内存
    free(str_b);
    return;
}

2. 右旋操作

右旋操作与左旋操作类似,但方向相反。右旋操作是指将字符串的后 n 个字符移动到字符串的开头。

例如,对于字符串 “AABCD”,右旋一个字符后得到 “DAABC”。

同理:右旋操作的实现步骤如下:

  • a. 创建一个临时数组来存储需要移动到开头的后 n 个字符。
  • b. 将剩余的字符向后移动 n 个位置。
  • c. 将临时数组中的字符复制到字符串的开头。
  • d. 释放临时数组占用的内存。

以下是右旋操作的代码实现:

void right(char* str, const int n) {
    assert(str != NULL);
    int size = (int)strlen(str);
    assert(n > 0);
    assert(n <= size);

    // 创建临时数组
    char* str_b = (char*)malloc(sizeof(char) * n);
    if (str_b == NULL) {
        printf("内存不足");
        return;
    }

    // 另存后n个数
    for (int i = 0; i < n; i++) {
        str_b[i] = str[size - n + i];
    }

    // 把数往后推移n个
    for (int i = 0; i < size - n; i++) {
        str[size - 1 - i] = str[size - 1 - i - n];
    }

    // 将n赋值到目前数组前
    for (int i = 0; i < n; i++) {
        str[i] = str_b[i];
    }

    // 释放内存
    free(str_b);
    return;
}

通过这两个子功能,我们可以对字符串进行任意的左旋和右旋操作。接下来,我们将介绍在实现这些功能时需要注意的事项。

三、注意事项

在实现字符串的左旋和右旋操作时,需要注意以下几个问题:

1. 输入验证

在对字符串进行操作之前,必须确保输入的字符串指针不为空。此外,旋转的位数 n 必须大于 0 且小于等于字符串的长度。否则,可能会导致程序崩溃或未定义行为。

assert(str != NULL);
int size = (int)strlen(str);
assert(n > 0);
assert(n <= size);

2. 内存管理

在实现左旋和右旋操作时,我们使用了动态内存分配来存储临时数据。在操作完成后,必须释放这些动态分配的内存,以避免内存泄漏。

free(str_b);

3. 字符串比较

在判断两个字符串是否相等时,不能直接使用 == 运算符,因为这只会比较指针的地址,而不是字符串的内容。正确的做法是逐个字符比较,或者使用标准库函数 strcmp。

int i = 0;
while (str1[i] != '\0') {
    if (str1[i] != str2[i]) return 0;
    i++;
}
return 1;

4. 旋转次数优化

对于长度为 size 的字符串,最多只需要旋转 size - 1 次即可覆盖所有可能的旋转情况。因此,在判断字符串是否为旋转关系时,旋转次数应限制在 size - 1 以内。

5. 字符串复制

在对字符串进行操作时,需要特别注意字符串的结尾字符 \0。在复制字符串时,必须确保 \0 也被正确复制,以避免字符串截断或未定义行为。

四、题目分析解答

现在,我们已经介绍了左旋和右旋操作的实现,并讨论了需要注意的事项。接下来,我们将结合这些知识,解决题目中的问题:判断一个字符串是否为另一个字符串旋转后的字符串。

问题分析

题目要求判断字符串 s2 是否可以通过对字符串 s1 进行左旋或右旋操作得到。为了实现这一目标,我们需要:

  1. 检查两个字符串的长度是否相等。如果长度不同,则直接返回 0。
  2. 对 s1 进行左旋和右旋操作,分别生成所有可能的旋转字符串。
  3. 比较每次旋转后的字符串是否与 s2 相等。如果找到匹配的字符串,则返回 1;否则,返回 0。

代码实现

#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>

void left(char* str, const int n);
void right(char* str, const int n);
_Bool check2(const char* str1, const char* str2);
_Bool check(const char* str1, const char* str2);

int main() {
    char str1[] = "1234567890";
    char str2[] = "9012345678";

    if (check(str1, str2)) printf("YES");
    else printf("NO");

    return 0;
}

void left(char* str, const int n) {
    assert(str != NULL);
    int size = (int)strlen(str);
    assert(n > 0);
    assert(n <= size);

    // 创建临时数组
    char* str_b = (char*)malloc(sizeof(char) * n);
    if (str_b == NULL) {
        printf("内存不足");
        return;
    }

    // 另存前n个数
    for (int i = 0; i < n; i++) {
        str_b[i] = str[i];
    }

    // 把数往前推移n个
    for (int i = 0; i < size - n; i++) {
        str[i] = str[i + n];
    }

    // 将n赋值到目前数组后
    for (int i = 0; i < n; i++) {
        str[size - n + i] = str_b[i];
    }

    // 释放内存
    free(str_b);
    return;
}

void right(char* str, const int n) {
    assert(str != NULL);
    int size = (int)strlen(str);
    assert(n > 0);
    assert(n <= size);

    // 创建临时数组
    char* str_b = (char*)malloc(sizeof(char) * n);
    if (str_b == NULL) {
        printf("内存不足");
        return;
    }

    // 另存后n个数
    for (int i = 0; i < n; i++) {
        str_b[i] = str[size - n + i];
    }

    // 把数往后推移n个
    for (int i = 0; i < size - n; i++) {
        str[size - 1 - i] = str[size - 1 - i - n];
    }

    // 将n赋值到目前数组前
    for (int i = 0; i < n; i++) {
        str[i] = str_b[i];
    }

    // 释放内存
    free(str_b);
    return;
}

_Bool check(const char* str1, const char* str2) {
    // 避免空指针
    assert(str1 != NULL);
    assert(str2 != NULL);

    // 获取字符串大小,检查字数是否相同
    int size1 = (int)strlen(str1);
    int size2 = (int)strlen(str2);
    if (size1 != size2) return 0;

    // 复制数组
    char* str2_ = (char*)malloc(sizeof(char) * (size1 + 1));
    if (str2_ == NULL) {
        printf("内存不足");
        return -1;
    }

    // 检查左旋
    for (int i = 1; i < size1; i++) {
        strcpy(str2_, str2);
        left(str2_, i);
        if (check2(str1, str2_)) { free(str2_); return 1; }
    }

    // 检查右旋
    for (int i = 1; i < size1; i++) {
        strcpy(str2_, str2);
        right(str2_, i);
        if (check2(str1, str2_)) { free(str2_); return 1; }
    }

    free(str2_);
    return 0;
}

_Bool check2(const char* str1, const char* str2) {
    assert(str1 != NULL);
    assert(str2 != NULL);

    int i = 0;
    while (str1[i] != '\0') {
        if (str1[i] != str2[i]) return 0;
        i++;
    }
    return 1;
}

代码分析

  • 主函数:在主函数中,我们定义了两个字符串 str1 和 str2,并调用 check 函数判断它们是否为旋转关系。
  • 左旋和右旋函数:这两个函数分别实现了字符串的左旋和右旋操作。它们通过临时数组存储需要移动的字符,并通过循环实现字符的移动。
  • 检查函数:check 函数首先检查两个字符串的长度是否相等。如果长度不同,则直接返回 0。然后,它通过循环分别尝试左旋和右旋操作,并调用 check2 函数比较旋转后的字符串是否与目标字符串相等。
  • 字符串比较函数:check2 函数逐个字符比较两个字符串是否相等。

通过以上实现,我们可以高效地判断两个字符串是否为旋转关系。

五、拓展应用

字符串的左旋和右旋操作不仅限于解决上述问题,它们在实际应用中还有许多其他用途。以下是一些拓展应用的例子:

  1. 字符串加密
    在某些简单的加密算法中,可以通过对字符串进行多次旋转操作来实现加密。例如,将字符串左旋 3 个字符,再右旋 2 个字符,最后再左旋 1 个字符。解密时,只需按照相反的顺序进行旋转操作即可。
  2. 字符串匹配
    在字符串匹配问题中,旋转操作可以用于生成所有可能的子串。例如,对于字符串 “ABCD”,通过旋转可以生成 “ABCD”、“BCDA”、“CDAB” 和 “DABC”。这些子串可以用于模式匹配算法中,提高匹配效率。
  3. 环形队列
    在环形队列的实现中,可以使用字符串的旋转操作来模拟队列的入队和出队操作。通过左旋和右旋操作,可以高效地移动队列中的元素,而无需额外的内存分配。
  4. 数据校验
    在数据传输中,可以通过对数据进行旋转操作并计算校验和,来验证数据的完整性和一致性。例如,将数据字符串左旋 5 个字符后计算校验和,接收方在接收到数据后,同样进行左旋 5 个字符并计算校验和,如果两者一致,则说明数据传输正确。
  5. 游戏开发
    在游戏开发中,旋转操作可以用于生成迷宫、地图或其他复杂的图形结构。例如,通过旋转字符串数组,可以生成不同方向的迷宫路径,增加游戏的趣味性和挑战性。

总结

通过以上对字符串左旋与右旋操作的详细讨论,我们不仅解决了题目中的问题,还掌握了字符串操作的核心技巧。左旋和右旋操作在实际应用中具有广泛的应用场景,从简单的字符串处理到复杂的算法设计,它们都扮演着重要的角色。
如果你对其他C语言问题感兴趣,欢迎关注我的专栏,我会定期分享更多有趣的编程题目和技巧。感谢你的阅读,我们下次再见。关注窝,每三天至少更新一篇优质c语言题目详解~

[专栏链接QwQ] :⌈c语言日寄⌋CSDN
[关注博主ava]:siy2333
感谢观看~ 我们下次再见!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/970774.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于单片机的开关电源设计(论文+源码)

本次基于单片机的开关电源节能控制系统的设计中&#xff0c;在功能上设计如下&#xff1a; &#xff08;1&#xff09;系统输入220V&#xff1b; &#xff08;2&#xff09;系统.输出0-12V可调&#xff0c;步进0.1V; &#xff08;3&#xff09;LCD液晶显示实时电压&#xff…

日常知识点之遗留问题梳理(被问到用uml画设计模式)

好多年不接触uml了&#xff0c;有一天面试&#xff0c;让用uml画出设计模式&#xff0c; 已经对uml的概念很模糊&#xff0c;隐约记得就是用例图&#xff0c;类图之类的&#xff0c;后面确定后&#xff0c;就是类图&#xff0c;用例图&#xff0c;时序图&#xff0c;都属于uml…

索引以及索引底层数据结构

一、什么是索引&#xff1f; 索引&#xff08;index&#xff09;是数据库高效获取数据的数据结构&#xff08;有序&#xff09;。在数据之外&#xff0c;数据库系统还维护着满足特定查找算法的数据结构&#xff08;B树&#xff09;&#xff0c;这些数据结构以某种方式指向真在…

搭建Deepseek推理服务

概述&#xff1a; 本文介绍用Open webui ollama搭建一套Deepseek推理服务&#xff0c;可以在web页面上直接进行对话。作为体验搭建的是Deepseek 7b参数版本 首先选择一个云厂商创建一台ubuntu系统的虚拟机&#xff0c;带公网IP&#xff0c;通过shell登录虚拟机完成以下操作&…

如何在C++中使用YOLO模型进行目标检测

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…

【第10章:自然语言处理高级应用—10.4 NLP领域的前沿技术与未来趋势】

各位技术探险家们,今天我们要开启一场穿越语言智能奇点的时空之旅。从正在改写物理定律的万亿参数大模型,到能看懂《星际穿越》剧本的跨模态AI,再到正在颠覆编程方式的神经-符号混合系统……这篇万字长文将带你摸清NLP技术进化的七块关键拼图。(建议边读边做笔记,文末有技…

SpringBoot+微信小程序+数据可视化的宠物到家喂宠服务(程序+论文+讲解+安装+调试+售后等)

感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;我会一一回复&#xff0c;希望帮助更多的人。 系统介绍 在经济高速发展、物质生活极大丰富的当下&#xff0c;人们的精神需求愈发凸显&#xff0…

案例-06.部门管理-根据ID查询

一.根据ID查询-接口文档 二.根据ID查询-Controller层 package com.gjw.controller;/*** 部门管理Controller*/import com.gjw.anno.Log; import com.gjw.pojo.Dept; import com.gjw.pojo.Result; import com.gjw.service.DeptService; import com.gjw.service.impl.DeptServi…

C++17中的LegacyContiguousIterator(连续迭代器)

文章目录 特点内存连续性与指针的兼容性更高的性能 适用场景与C接口交互高性能计算 支持连续迭代器的容器示例代码性能优势缓存局部性指针算术优化 注意事项总结 在C17标准里&#xff0c;LegacyContiguousIterator&#xff08;连续迭代器&#xff09;是一类特殊的迭代器。它不仅…

【Kubernetes】k8s 部署指南

1. k8s 入门 1.1 k8s 简介 需要最需要明确的就是&#xff1a;kubernetes&#xff08;简称 k8s &#xff09; 是一个 容器编排平台 &#xff0c;换句话说就是用来管理容器的&#xff0c;相信学过 Docker 的小伙伴对于容器这个概念并不陌生&#xff0c;打个比方&#xff1a;容器…

Redis 03章——10大数据类型概述

一、which10 &#xff08;1&#xff09;一图 &#xff08;2&#xff09;提前声明 这里说的数据类型是value的数据类型&#xff0c;key的类型都是字符串 官网&#xff1a;Understand Redis data types | Docs &#xff08;3&#xff09;分别是 1.3.1redis字符串&#xff0…

基于矩阵分解-协同过滤推荐算法的视频播放平台【源码+部署+论文】

作者简介&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流。✌ 主要内容&#xff1a;&#x1f31f;Java项目、Python项目、前端项目、PHP、ASP.NET、人工智能…

FPGA的星辰大海

编者按 时下风头正盛的DeepSeek,正值喜好宏大叙事的米国大统领二次上岗就业,OpenAI、软银、甲骨文等宣布投资高达5000亿美元“星际之门”之际,对比尤为强烈。 某种程度上,,是低成本创新理念的直接落地。 包括来自开源社区的诸多赞誉是,并非体现技术有多“超越”,而是…

「AI学习笔记」机器学习与深度学习的区别:从技术到产品的深度解析(四)...

随着人工智能&#xff08;AI&#xff09;的快速发展&#xff0c;机器学习&#xff08;ML&#xff09;和深度学习&#xff08;DL&#xff09;已经成为我们日常生活中不可忽视的技术力量。无论是推荐系统、语音助手&#xff0c;还是自动驾驶汽车&#xff0c;它们背后都离不开ML和…

MATLAB图像处理:图像分割方法

图像分割将图像划分为具有特定意义的子区域&#xff0c;是目标检测、医学影像分析、自动驾驶等领域的核心预处理步骤。本文讲解阈值分割、边缘检测、区域生长、聚类分割、基于图的方法等经典与前沿技术&#xff0c;提供MATLAB代码实现。 目录 1. 图像分割基础 2. 经典分割方…

动手实现一个PDF阅读器

1、简介 使用 pdf.js 库加载和显示 PDF 文件。 实现了翻页、缩放功能。 提供了基本的错误处理。 功能特点&#xff1a; 支持选择本地 PDF 文件。 可以逐页查看 PDF 内容。 支持放大缩小功能。 界面简洁&#xff0c;易于使用。 2、使用方法 <!DOCTYPE html> <html la…

利用亚马逊AI代码助手生成、构建和编译一个游戏应用(下)

在上篇文章中中&#xff0c;我们介绍了如何通过亚马逊AI代码生成助手 - Amazon Q Developer代理的代码生成、构建和测试功能&#xff0c;让开发者可以更高效地交付高质量代码项目&#xff0c;同时减少代码中bug错误&#xff0c;提升整体开发体验。在本篇中&#xff0c;我们将通…

unity学习42:动画状态机:混合动画状态 blend tree

目录 1 动画状态机 1.1 新建动画状态 2 混合动画状态 blend Tree 2.1 new blend Tree 2.2 blend tree state 和普通的 state的属性不同 2.3 双击blend tree 进入下一层 blend tree内部 2.3.1 blend tree 内部 2.3.2 blend type 2.3.3 参数类型默认是float&#xff0…

ipfs安装及其访问webui

在区块链应用场景里&#xff0c;常常需要借助专门的存储系统来保存各类文件。IPFS&#xff08;星际文件系统&#xff0c;InterPlanetary File System&#xff09;便是一种适用于区块链网络的分布式存储解决方案&#xff0c;它能够让用户便捷高效地存储和管理文件。 下面&#…

全方位探索DeepSeek

目录 前言1. DeepSeek的基础功能与应用场景2. 使用DeepSeek的多种方式2.1 通过Web界面快速体验2.2 调用API实现自动化处理2.3 集成到本地开发环境2.4 结合第三方工具扩展功能 3. 高效使用DeepSeek的进阶技巧3.1 参数调优与性能优化3.2 数据处理与结果分析 4. 实际案例分析与应用…