学透字符串的旋转

  • Part I、前言
  • Part II、左/右旋
    • 1、定义
    • 2、共同特点
  • Part III、初阶解法
    • 解法一:创建新数组
    • 解法二:原地算法(直接法)
  • Part IV、进阶解法
    • 1、 三步反转法
    • 2、找子串法
  • Part V、写在最后

Part I、前言

每天学一点,进步多一点!
今天的主题:string rotation 字符串的左/右旋

Part II、左/右旋

1、定义

其实用文字比较难描述出来,我们用例子能更加简单的理解:
给出字符串:ABCDE
左旋一次:BCDEA
左旋两次:CDEAB
左旋三次:DEABC
左旋四次:EABCD
左旋五次:ABCDE
我们注意到,左旋K次,就是把前K个字符整体挪动到字符串的右边,注意,是整体挪动,这意味着挪动的部分,它的顺序与原先相同。
我们还注意到,经过五次左旋,字符串回到了初始的样子。(这句不是废话哦)

同样是字符串:ABCDE
右旋一次:EABCD
右旋两次:DEABC
右旋三次:CDEAB
右旋四次:BCDEA
右旋五次:ABCDE
与左旋类似,我们将后K个字符整体挪动到字符的左边,顺序不变。
我们同样注意到,经过五次右旋,字符串回到了初始的样子。

2、共同特点

特点一:无论是左旋K次还是右旋K次,无非就是将左边的或者右边的K个连续字符,整体的进行位置的挪动,概括下来就是:左旋就是左边整体右挪,右旋就是右边整体左挪,且顺序不变;
特点二:当旋转的次数K达到某一特定值时,字符串维持原样,相当于没有进行旋转,而且我们不难发现,也不难论证出:这个特定值,就是字符串的长度(将整个字符串整体挪动,自然是维持原样)

Part III、初阶解法

由于左右旋具有许多共同特性,考虑到篇幅问题,我们主要讨论左旋的情况,右旋的话,简单改变一下代码中的某些控制量就OK啦~

解法一:创建新数组

我们根据上面的特点1,将后len-K%len个存到一个数组ret中,然后将前K个存到数组ret的剩余位置。

K%len是什么意思? 【K:旋转次数 len:字符串长度(不包括空字符’\0’)】
根据我们先前的特点二:“当旋转的次数K达到字符串长度len时,字符串维持原样,相当于没有进行旋转”
所以当我们让len÷K,得到的结果的整数部分,意味着我们要对这个字符串整体挪动这整数部分次,这并没有对字符串造成实质性的影响
而余数部分,才是我们需要考虑的真正有用的那几次旋转——也就是K%len次;

理解了做法后,我们代码实现一波:

char* left_rotation(char arr[], int len, int K)
{char* ret = (char*)malloc((len + 1) * sizeof(char));if (ret)//malloc成功{int i;for (i = 0; i < len - K % len; i++)//将后len-K%len个存到一个数组ret中{ret[i] = arr[i + K % len];}for (i = 0; i < K % len; i++)//将前K个存到数组ret的剩余位置{ret[i + len - K % len] = arr[i];}ret[len] = '\0';return ret;}else//malloc失败,返回空指针{return NULL;}
}

注意:调用该函数后需要将返回的指针free掉,避免内存泄漏!

解法二:原地算法(直接法)

原地算法,顾名思义,就是通过覆盖的方式,在arr的内部直接进行旋转操作,同样,我们拿左旋举例——
我们将arr的第一个字符取出,然后拿后方的数据覆盖前面的数据,最后再把取出的字符接到数组的末尾,重复该操作K%len次。
【注:这里的K%len参考上面的解释哦~】
代码实现:

void left_rotation(char arr[], int len, int K)
{int k=K%len;//执行次数while (k--){int i, tmp = arr[0];//取出数组的第一个数for (i = 0; i < len-1; i++){arr[i] = arr[i + 1];//后方数据覆盖前方数据}arr[len - 1] = tmp;//将取出的数放到最后一个}
}

Part IV、进阶解法

1、 三步反转法

我们刚刚说过,左旋的本质实际上就是:将前面的K%len个整体挪动到后方,将后面的挪到前方,也就是交换前后两部分的位置,并且要求它的顺序与原先一致;
借鉴大神的一种解释方式:
我们原先的数组是这样的:

我们将其分为两个部分:

我们想要的结果是这样:

而想要做到这样,我们只需要反转S1和S2:

然后整体反转:

surprise!目的达成。
这样的做法有一定的数学依据(经评论区老铁建议后修改)——
学过线性代数的知道:当我们假定数组为AB,A代表S1,B代表S2,想要得到BA
只需要:(AT·BT)T
也就是说,对A和B分别求转置矩阵后,在整体求转置矩阵!与这里三次反转如出一辙!
代码实现

void Reverse(char arr[], int left, int right)
{while (left < right){char tmp = arr[left];arr[left] = arr[right];arr[right] = tmp;left++;right--;}
}
void left_rotation(char arr[], int len, int K)
{Reverse(arr, 0, K % len-1);Reverse(arr, K % len, len-1);Reverse(arr, 0, len-1);
}

2、找子串法

这个方法同上面的方法一样,很难想到,但是也很好理解!
本解法灵感来自这道题的一道改编题:给定一个字符串,判断其是否能通过另一个字符串的旋转得到。
比如:CDEFAB可以通过字符串ABCDEF左旋两个字符得到。
对于这道题的做法,我们采取接长字符串ABCDEF的方法,也就是说,我们在ABCDEF的后面再接上一个ABCDEF,让其变成:ABCDEFABCDEF,此时,我们会惊奇的发现,由于字符串旋转的性质一,ABCDEF这个字符串所有可能的旋转结果都是这个接长后的字符串的子串!
如果将这种做法放到这道题上,我们不难看出:
假设字符串长度为len,左旋次数为K,那么这个接长字符串中下标为K%len的就是我们所需要的那个旋转后的字符串的首地址!
比如:ABCDEF左旋两次,就是ABCDEFABCDEF中下标为2的,就是我们需要返回的字符串的首地址。
代码实现:

char* left_rotation(char arr[], int len, int K)
{strncat(arr,arr,len);//strncat函数可以在数组后追加自己arr[K%len + len] = '\0';return arr + K%len;//返回下标为K的那个地址
}

这个函数的使用需要确保arr有足够的空间来追加自己!

Part V、写在最后

一道简单的C语言题目,从第一反应的初阶解法再到进阶解法,真的不得不感叹:想出这种解法的人有多聪明!反正,笔者是真的相当佩服!

不知道这篇文章对你是否有所帮助呢?你又是否对字符串的旋转有什么新的见解?欢迎评论区赐教!
这是《每天学一点系列~》的第一篇,后续还会有更新,喜欢的话,欢迎给个三连哦!
by白龙码

【每天学一点系列~】字符串左/右旋的本质,你真的认清了嘛?相关推荐

  1. 字符串左旋右旋——三步旋转法和移相法

    题目:实现一个函数,可以左旋字符串中的k个字符. AABCD左旋一个字符得到ABCDA AABCD左旋两个字符得到BCDAA 方法一:三步旋转法 左旋程序思路:首先根据画图得知左旋后的结果,然后在分析 ...

  2. 什么都学一点系列之鸿蒙开发Java版简易备忘录

    本文地址:https://blog.csdn.net/qq_40785165/article/details/123908357,转载请附上此链接 .生活不会惯着你,想要不被抛弃,必须自己争气. 大家 ...

  3. 【每天学一点系列~】还在困惑数据结构(尤其是链表)里指针的看这里!!!

    一定要搞定指针啊!!! Part I.说在前面儿 Part II.指针它是个啥? 1.地址 2.指针的指向作用 2.地址与数据 3.解引用 4.二级指针 5.传址调用 Part III.数据结构里的指 ...

  4. 字符串的左旋右旋问题(C语言实现,三种方法求解)

    字符串左旋右旋问题其实是同理的,下边以左旋为例: 方法一 思路:左旋一次就是将整个字符串向左移一个字符,第一个字符(arr[0])移动到最右侧.这样循环操作左旋次数就是最终左旋结果,如上图所示. 实现 ...

  5. 字符串的左旋右旋问题

    字符串的左旋右旋其实是一个问题,下面只对右旋进行分析. 当一个字符串进行旋转的时候,字符串的大小会决定有效旋转次数.这里面的有效是指,后面的旋转会与前面的旋转相同,所以我们必须要对旋转次数进行去重. ...

  6. 左旋右旋问题一次搞定!!!

    左旋右旋问题的解决 编程思想 1.在左右旋函数中实现该函数功能首先要想好如何存放移位后的字符元素 2.左旋时将字符串首元素赋给临时变量tmp而后将字符串元素依次前移一位 3.将tmp的值再赋给字符串的 ...

  7. 字符串从右截取_跟运维组学Python基础day04(字符串str的索引和切片)

    内容回顾 跟运维组学Python基础 day03 格式化输出 %s name = input('Pleases input your name: ') # Zanaoprint('My name is ...

  8. 实现字符串左旋和右旋的常见方法

    说起字符串的左旋和右旋问题,想必大家都不陌生,这是一个在初学C语言过程中经常遇到的一个问题,解题的思路可以说很多,每一个人的看待问题的角度都不同,所以就可以得到不同的解题思路.下面我就列举几种方法: ...

  9. python右对齐输出如果结果超过30个字符_Python3 输出字符串左对齐、右对齐、居中对齐...

    1.通过ljust(),center(),rjust()函数实现输出的字符串左对齐.居中.右对齐 方法一:使用函数默认不带参数,则默认以空格填充(文字与空格总字符数等于输入的数字) #代码 print ...

最新文章

  1. Python 入门建议
  2. MySQL性能优化最佳实践 - 02 MySQL数据库性能衡量
  3. yarn资源管理调度平台
  4. 阿里腾讯前端一面小结
  5. 导出Oracle数据库字典
  6. c语言表达式10 3的结果是,C语言程序设计--第3讲运算符与表达式.ppt
  7. 代码生成利器-NCodeGenerate 教程(2) NCodeGenerate的代码公用之一
  8. 要把人工智能提速50倍的ARM,却依然坚持做“通用的计算架构”
  9. 使用Safari浏览器自带工具,查看页面中 css 样式的引用~
  10. IIC加载以及存在的问题---lattice XO3
  11. Oracle项目管理系统的BIM应用
  12. 强东变法——京东能否逢凶化吉?
  13. 如何让自己的电脑内外网同时用?
  14. There are 7 missing blocks. The following files may be corrupted
  15. VBA -[知识点]: 字典
  16. testtesttesttest
  17. 基于ESP32+AMG8833的物联网红外成像测温枪
  18. stm32常用数据类型 U8、U16、U32到底代表什么?
  19. 深度强化学习+启发人类的决策智能,专访一家有愿景的中国企业「启元世界」... 1
  20. 动态GIF表情怎么制作

热门文章

  1. MapKit 以及大头针的简单使用
  2. 618淘宝星秀猫自动完成任务
  3. sqlserver设置两个及两个以上主键
  4. 在C++中两个指针相加有意义么?
  5. css 布局和选择器
  6. 关于spring事务传播行为引发的Transaction rolled back because it has been marked as rollback-only
  7. 代驾小程序源码全套,支持二开(Thinkphp+bootstrap+小程序uniapp)
  8. 计算机图形编程基础,Windows图形编程基础.ppt
  9. 中国最火开博啦~~~
  10. 如何在 Linux 中检查我的网卡速度?