旋转字符串的秘密

      最近离谱张童鞋对字符串旋转,翻转等问题非常感兴趣,看了些视频以及做了一些题目,现在有一些新的想法以及后续学习的目标以及规划要分享给大家. 

目录:

1:介绍一下整体旋转字符串的2种方法
2:旋转单个字符,设计一个函数(两种方法,第一种是设计函数第二种是三步翻转法:特别巧妙,这里将会画图详细介绍.
3:题目1:旋转一个字符串然后单词也逆序.:

4:查找字符串,本题会把思路和代码详细的讲一遍.
好,接下来咱们先看这样一个问题

题目 :完成对一个字符串的逆序.
示例:abcd
输出:dcba
(偶数个)

示例:abcde
输出:edcba
(奇数个)

思路1:可以用循环来实现,把b一个字符串传入数组中,既然是数组,就可以用访问下标的形式来进行访问了,然后再倒序输出就行了
废话不多说,直接上代码(别急每一条代码后面有注释喔)

​
/*
方法1:for循环倒序输出:

思路:和倒序输入数字一样的,直接倒序打印就可以

*/

//已在编译器上测试过,没问题
#include <stdio.h>
#include <string.h>
int main()
{
    char s[1000]={0};//创建一个数组用来存放字符串,大小的话,注意题目规范,还有变量初始化,好习惯一定要养成
    scanf("%s",s);//输入字符串,也可以用gets
    int n=strlen(s);//求出有几个字母,顺便可以算出下标
    int i=0;//变量初始化,一定别忘了喔
    for(i=n-1;s[i]!='';i--)//从最后一个字母的下标开始倒序输出.
    {        
        printf("%c",s[i]);
              
      }
        
    return 0;
  }

​

总结:这就是思路1啦,不难理解,接下来我们看思路1(2).

/*

方法2:也是用循环来完成的,但不过这个是用数组来实现字符串交换的,和这个题目有一些些相似,但是本质也还是相同的

这个和鹏哥之前的字符串汇聚蛮相似的,所以我想把他们放到一起来看


*/
//已在编译器上测试过,没问题
#include<stdio.h>
#include<string.h>
int main()
{
    char arr[1000]={0};
    int left=0,right=0,i=0;
    scanf("%s",arr);//输入字符串的时候用gets也是可以的哟
    int n=strlen(arr);//这样我是把这样条件都一股脑的写在了for循环里面,大家也可以写在外面
    for(left=0,right=n-1;(right-left)>0;left++,right--)
    {
/*
1:这里注意一点就是,right-left>1可以不可呢?答案是不可以,因为如果你交换的是偶数的话,第五和第六个元素,6-5=1,条件不满足,就退出来了,所以还是当右下标大于左下标的时候,这样就可以啦

2:然后就可以实现字符串的交换了哟
先创建一个临时变量用来存放左下标的元素.这就和交换三个数是一模一样的,大家类比就好啦
由于交换的是字符类型,所以,一定得用char型来创建临时变量的哟

*/
       char t=0;
       t=arr[left];
       arr[left]=arr[right];
       arr[right]=t;
       
      }

//交换完毕之后,最后打印出来就好啦

    printf("%s",arr);
  
   return 0;
 }
 

现在我们来回顾一下字符串汇聚的那个题目把:题目所示:
编写代码,演示从多个字符从两段移动,向中间汇聚。

思路如下(字多,详细,请大家耐心阅读):
比如我让arr2的#号变成welcome bit

首先得创建两个数组arr1和arr2来存储这两个字符串儿

然后字符串的话,已经存放到了数组里面了,然后就可以采用访问数组下标的形式,定义左下标为0,右下标为sz-1.
,但是他不能算sz,因为他是个字符串,这时候就要了解一下strlen函数了,str是string是字符串,len是lengh是表示长度的的意思,所以,这个是strlen函数是这么由来的

那么所以left和right都应该等于什么呢?
int left=0;
int right=strlen(arr2)-1;

首先明确题目,是让arr1的每一个元素来传到arr2中对吧,所以就是让arr1的左下标来放入到arr2的左下标,同理的右坐标也是一样的.

因为咱们是每传一次,咱们看一下他这个变化对吧,就是在交换了之后面加一个打印就行了,

然后这个是一次,我让left++,和right减减,他是不是就可以向中间汇聚了呢?

所以是不是得加一个while循环啊,是不是,这个和二分查找是一样的,二分查得查找很多次啊.

好了,思路以及有了,代码如下

#include <stdio.h>
#include <stdio.h>
#include <string.h>
//已经在编译器上测试过
int main()
{
    char arr1[]="welcome bit";
    char arr2[]="###########";
    int left=0;
    int right=strlen(arr2)-1;
    while(left<=right)
    {
        arr2[left]=arr1[left];//一定要注意,不是(),是[]
        arr2[right]=arr1[right];
        printf("%sn",arr2);//这样就每打印一行,加一下,效果是从中间汇聚的.
      //  sleep(1000);//手机上不能实现,停留,因为手机没有windows.h
        left++;
        right--;
    }
    return 0;
   }

 好了,回过神来了,好了这个数组交换的方法,大家肯定会了,然后现在来看函数是如何实现的吧

方法三,用一个函数来实现,这个函数我们就把他定义为字符交换函数把,

字符交换函数

思路就是:要想交换一个字符串,就得把这个字符串的首尾地址传给这个交换函数,然后用指针的形式来进行交换,而我们定义的这个函数就得用两个指针来进行接收啦

好了,这个思路大致有了以后,咱们就来说敲代码了嗷

(1):首尾地址>可以用数组名来表示,因为数组名就代表着首元素的地址,而末尾元素的地址,则是字符串长度-1

(2):当把参数传给自定义函数里面,我们可以用左下标指针和右下标指针来进行接收他们,

(3)可以用while循环,或者是for循环来进行交换,

那条件是什么呢,我们可以这样想,就是当我们的左下标<=右下标的时候,这样是不是就停止了交换了呢?

好,思路有了,代码如下:

#include<stdio.h>
#include<string.h>
void my_string_reverse(char*left,char*right)
{
//交换,这里的*和[]是一样哒
    while(left<right)
    {
        char t=*left;
        *left=*right;
        *right=t;
//交换这一步一定要写对
        left++;
        right--;
     }
       
}    
int main()
{
    char arr[1000]={0};//好习惯的初始化
    scanf("%s",arr);
    int n=strlen(arr);
    my_string_reverse(arr,arr+n-1);//传首尾元素的地址
//完成之后呢,我们来打印一下就ok啦,来看看他是否完成了交换
    printf("%s",arr); 
    return 0;
 }

ok,这是整体交换的方法,我们再来聊聊,更近一步的,如何才能使交换一串儿字符的两个字符呢?这样可以是左交换,或者是右交换;

题目如下:加照片

 

其实每一个题目的思路都可以是这样的,首先要明确题目内容是什么,要把某个东西干什么,最终要完成什么目的,可以用到的工具是什么,依次来进行思路的搭建,

交换一个字符串中的两个(k个)字符,也称为左旋字符串.

要旋转的字符串会发生改变,所以我们可以存放在数组中,因为数组元素才可以被改变,
万万不可用char*p="ABCDEF"来改变常量字符串.

然后我们就要自定义函数啦,my_string_left(arr,k)再把arr这个数组传过去,k为旋转的个数,
然后用char*str来接收arr,int k来接收k.

既然传参部分都解决了,那么我们就开始想他如何实现吧,

现在有一个字符串ABCDEF,旋转的话我们可以这样想,把其中的一个A字母用一个临时变量存起来,然后BCDEF整体往前移动,往前移动嘛,那就是用一个for循环a[i-1]=a[i](如果是下标为0的,格式还可以这么写a[i]=a[i+1]就可以搞定啦,

最后在这个循环里面完成交换之后,把这个临时变量再放回到最后一位就行了。

好了,这就是大体思路,现在我们转为代码看看呗,


#include<stdio.h>
#include<string.h>
void my_string_left(char*str,int k)
{
    int i=0,j=0;//初始化
    int n=strlen(str);//st为首元素的地址,这里不懂的话,可以去查一查strlen接收的参数类型,这里为大家奉上:unsigned int strlen(const char *str);很明显,str是字符型指针,一会儿得接收地址
  
    for(i=0;i<k;i++)//在for循环里面完成交换,一共有n个元素,现在拿出去第一个元素用于以后的存放,还剩下n-1要向前移动,然后要想把长度求出来,就得用strlen来求一下
    {
        char t=*str;//首先:把第一个元素存放到一个临时变量里面,放到for循环里面的目的是为了本次循环结束之后,下次循环,获得的是新的一个字符,如果放在for循环外面的话,每次获取的斗只是第一个字符
        for(j=0;j<n-1;j++)
        {
            *(str+j)=*(str+j+1);//其次:这里的*(str)是解引用,把找第一个元素的地址来找到第一个首元素,而且不能没有*str,如果没有首元素的地址那你怎么能找到末尾的元素呢?      
         }
     
    //最后:空出的这个位置有了,那就存放第一个元素吧
        *(str+n-1)=t;             
     }
}  
 
int main()
{
    
   char arr[1000]={0};//初始化,好习惯
   scanf("%s",arr);//输入字符串的时候,可不需要取地址喔,而且还可以用gets
   int k=2;//把要旋转的字符传过去
//传左旋
   my_string_left(arr,k);//自定义函数
   printf("%s",arr);//最后打印
   
   return 0;
 }

好的,那么既然左旋会了,那右旋是不是也一样呢,这里大家先自行思考一会儿,不许看代码喔嘿嘿

右旋:同左旋一一模一样的,稍微改变一下就行.
大家自行思考,再看代码

右旋字符串

#include <stdio.h>
#include<string.h>
void my_string_right(char*str,int k)
{
    int i=0,j=0;
    int n=strlen(str);//str为首元素的地址,这里不懂的话,可以去查一查strlen接收的参数类型,这里为大家奉上:    
    for(i=0;i<k;i++)//第二步,在for循环里面完成交换,一共有n个元素,现在拿出去第一个元素用于以后的存放,还剩下n-1要向后移动,然后要想把长度求出来,就得用strlen来求一下
    {
        char t=*(str+n-1);//首先把最后一个元素存放到一个临时变量里面,放到for循环里面的目的是为了本次循环结束之后,下次循环,获得的是新的一个字符,如果放在for循环外面的话,每次获取的只是第一个字符
        for(j=n-2;j>=0;j--)
        {
            *(str+j+1)=*(str+j);//往后移动啦    
         }
     
    //第三步:空出的这个位置有了,那就存放最后一个元素吧
        *(str)=t;
             
     }
}  

int main()
{
    char arr[1000]={0};
   scanf("%s",arr);//输入字符串的时候,可不需要取地址喔,而且还可以用gets
   int k=2;//把要旋转的字符传过去
//右旋两个
   my_string_right(arr,k);//自定义函数喔
   printf("%s",arr);

   
     return 0;
 }

好了,思路回来,以上的两个代码,都是旋转一个字符串的左边或者右边的两个字符,但是我们这样想一下,右旋一个字符=左旋三个字符
eg1:
abcd >> 右旋一个 >>dabc
== 左旋三个>>dabc

eg2:
abcd >> 右旋二个>>cdab
== 左旋六个>>cdab
已经测试过了,没问题的.兄弟们,放心用

接下来让我我们回到那个最初的问题上…
接下来给大家介绍一种方法,三步翻转法,这样也可以进行字符串的翻转,很妙,很值得品味

三步翻转法
1左翻转
2右翻转
3整体翻转
如图所示:

 

#include <stdio.h>
#include<string.h>
void my_string_right(char*str,int k)
{
    int i=0,j=0;
    int n=strlen(str);//str为首元素的地址,这里不懂的话,可以去查一查strlen接收的参数类型,这里为大家奉上:    
    for(i=0;i<k;i++)//第二步,在for循环里面完成交换,一共有n个元素,现在拿出去第一个元素用于以后的存放,还剩下n-1要向后移动,然后要想把长度求出来,就得用strlen来求一下
    {
        char t=*(str+n-1);//首先把最后一个元素存放到一个临时变量里面,放到for循环里面的目的是为了本次循环结束之后,下次循环,获得的是新的一个字符,如果放在for循环外面的话,每次获取的只是第一个字符
        for(j=n-2;j>=0;j--)
        {
            *(str+j+1)=*(str+j);//往后移动啦    
         }
     
    //第三步:空出的这个位置有了,那就存放最后一个元素吧
        *(str)=t;
             
     }
}  

int main()
{
    char arr[1000]={0};
   scanf("%s",arr);//输入字符串的时候,可不需要取地址喔,而且还可以用gets
   int k=2;//把要旋转的字符传过去
//右旋两个
   my_string_right(arr,k);//自定义函数喔
   printf("%s",arr);

   
     return 0;
 }

好啦,到这里,旋转字符串,不管是多个,还是任意个字符的旋转,大家肯定都会了,然后来一道题目来巩固一下吧

题目如下:

 

方法一:暴力穷举法:把能想到的可能都列出来,然后进行一一比对

就是判断s2是否由s1旋转得到的,就可以让s1先旋转一个,比一下,然后再旋转一个,比一下,这里用for循环就可.

外层for循环每次循环一次,就说明了已经完成了一次交换,这时候用strcmp函数来比较一下,看看是否相等,如果相等的话呢?那就跳出来就好了啦

 

#include <stdio.h>
#include <string.h>
#include <assert.h>
char is_pd_string_rotate(char*str1,char*str2)//最前面的类型,char可以,int也可以
//char*str1来接收arr1,char*str2来接收arr2
{
    assert(str1);
    assert(str2);
    int i=0,j=0;
    int n=strlen(str1);
    for(i=0;i<n;i++)
    {
        char t=*str1;//第一步:记录起始位置
        for(j=0;j<n-1;j++)
        {
            *(str1+j)=*(str1+j+1);//第二步往前移动:就是前面的比后面的小,那就往前移动
          
        }
       *(str1+n-1)=t;//第三步:把第一个元素放在最后面
//每一次完了之后进行一次比较,如果相同,那就返回1就好了啦     
      if(strcmp(str1,str2)==0)
      {
          return 1;
       }    
   }//这层for循环如果停止的话,说明已经旋转了每一个了,就没有匹配的,说明s2不是由s1旋转的来的,那咱们return 0就好了
   return 0;
   
}
int main()
{
    
    char arr1[]="AABCD";//记得数组一定一定要初始化喔
    char arr2[]="BCDAA";
    int ret=is_pd_string_rotate(arr1,arr2);
    if(ret==1)
    {
        printf("yes");
     }
    else
    {
        printf("no");
     } 
    
    
    
    return 0;
 }

好了,到这里,我们的代码就实现啦,还有一个注意点就是这个如图,大家一定写函数类型的时候要小心,一定要小心谨慎
不要犯我这个小错误

方法二:追加自己的字符串,判断字符串是否包含的问题

思路:就是我们能不能把这一个字符串来变化一下,让它包含所有的情况呢?,答案是可以的,

首先:我们用字符串追加就可以了。
其次:到了最后,判断arr2是不是arr1的子串儿就好了。


#include <stdio.h>
#include <string.h>
#include <assert.h>
char is_pd_string_rotate(char*str1,char*str2)//char可以,int也可以
{
    assert(str1);
    assert(str2);
    if(strlen(str1)!=strlen(str2))
    { 
//二者长度不一样,肯定旋转返回0就ok了
        return 0;
     }
//首先进行了字符串的追加,
       int len=strlen(str1);
       strncat(str1,str1,len);

    
//完成之后用strstr函数来看看是不是子串儿,如果是子串儿的话,就返回空指针(具体的话,大家可以自行查一下strstr函数
    char*ret=strstr(str1,str2);
    if(ret==NULL)
   {
       return 0;//那就是找不到子串儿
    }
    else
    {
        return 1;//如果找到了,那就返回1
     }
 //此时还有一个例外就是,如果他们的字符串长度原本就不想等,那么肯定不是旋转得来的
    //所以啊,在前面加一个if判断就好了啦  
   
}

int main()
{    
    char arr1[]="AABCD";//记得数组一定一定要初始化喔
    char arr2[]="BCDAA";
    int ret=is_pd_string_rotate(arr1,arr2);
    if(ret==1)
    {
        printf("yes");
     }
    else
    {
        printf("no");
     } 
    
        
    return 0;
 }

总结:本题用了C语言的库函数比较多,所以在执行方面和书写方面会比较快。比前面的自定义函数方便一些。

============================================================
接下来,我们来看下一道题吧(大家先自行思考10min再看代码)
题目如图所示:

 先说整体思路:

思路:1先写一个可以逆序的函数(咱们之前的文章已经有啦)(把这一句话的首元素地址和末尾元素的地址传过去)
2传过去之后,那就看看单个单词的逆序就好啦,

具体思路:
先把整个字符串逆序,设计了一个翻转函数,然后再逆序每一个单词,要逆序单词的话,就得有每一个单词的下标,
要下标的话,得拿指针(或者left,right下标来算,做法是最后把下标传递给数组来算的。然后就是用到找下标的
方法了,找下标的话,如果遇到’’的话那就是最后一个单词的末尾了,然后接下来就是每一个单词的后面的空格了
,如果循环到不等于空格的时候,那就继续往下找,直到遇到一个单词的空格,那就开始走else了,else的话,传的
就是左下标为0和右下标减一,这样就是这一个单词的逆序了,然后else完了之后,走for循环right++,这样就是
下一个单词了,依次类推,就完成了逆序。

方法是用到了for循环和数组来进行逆序

但是本质还是找到这个单词逆序的关键点还有如何找到下一个单词,以及空格和的处理

#include <stdio.h>
#include <string.h>
void my_string_fz_reverse(char a[],char left,char right)
{
//for前面的条件1就是char left来接受的0,和char right 接受的n-1
    for(;(right-left)>0;)//条件3是放到了后面,当然兄弟们放到前面也行
  {
     char t=a[left];
     a[left]=a[right];
     a[right]=t;
     
     left++;
     right--;  
    }    
}
int main()
{
    
    char s[10000]={0};//初始化为0,好习惯
    gets(s);
    int n=strlen(s);
//一次性传3个元素,1数组,因为上面要用for循环来实现翻转;2left左下标,3:right右下标
    my_string_fz_reverse(s,0,n-1);
    int left,right;
    for(left=0,right=0;s[right]!='';right++)
    {
//接下来的if如果不对懂的话,前面有详细的叙述,兄弟们可以翻过去好好看看
        if(s[right]!=' ') 
           continue;
        else
        {
            
            my_string_fz_reverse(s,left,right-1);
            left=right+1;
          }
          
     }
     my_string_fz_reverse(s,left,right-1);
     printf("%s",s);
    return 0;
  }

好啦,这就是方法一。
再来看方法二:

方法二:指针加循环和方法一差不多,只是换一种形式而已

#include<stdio.h>
#include<string.h>
void reverse(char*left,char*right)
{
    while(left<right)
    {
      char t=*left;
      *left=*right;
      *right=t;
      left++;
      right--;
    }
        
}    
int main()
{
    
   char arr[1000]={0};
   int left,right;
   get(arr);//输入一串儿单词
   int n=strlen(arr);
   reverse(arr,arr+n-1);//这是交换整体的逆序
//   然后再进行单个单词的逆序,就得用双指针
   char*start=arr;
   while(*start)
   {
       char*end=start;
       while(*end!=' ' && *end!='')
       {
            end++;//这样是地址的++,然后指向下一个地址;然后才能以便后续的解引用来进行找到元素
            
        }
       reverse(start,end-1);
       if(*end==' ')
         start=end+1;
       else
         start=end;       
     }
     
   printf("%s",arr);
    return 0;
 }

好了,这就是今天的内容了,最后这个题跟今天内容没有关系,但是可以很好的去锻炼我们的思维:
题目如下:

 

用三层for循环来进行一步一步的筛选
前提是注意for里面的内层嵌套还有if的使用

如果在本字符串中找到一个c,那就看看下一个是不是h,如果是h,那再看看是不是n,如果他们都满足的话,count++,这个一个chn,同理其他情况.

如图所示:

 方法一:三层for循环的嵌套以及if语句和continue的用法


/*
方法一:用三层for循环来进行一步一步的筛选
前提是注意for里面的内层嵌套还有if的使用


eg:要找CCHNCHN中的CHN的个数,先找C,然后再进行判断,下一个字母如果是H,就往下走,如果不是的话,

就第一层for循环在进行下一个单词的查询,当找到H之后,再定位到下一个单词看看是不是N,如果是N的话,

那就count++,进行基数,如果第一个H的后面仍然有N的话,那z循环就会继续往后找,continue的作用就是

这样,跳过本次循环,进行下一次循环,如果下一次循环条件满足的话,就进行判断,再看看是否满足z循环,




*/
#include <stdio.h>
int main()
{
   int count=0,i=0,j=0,z=0;
   char s[100]={0};
   scanf("%s",s);   
   for(i=0;s[i]!='';i++)
   {
       if(s[i]!='C')
         continue;
       if(s[i]=='C')
      {
          for(j=i+1;s[j]!='';j++)
          {
              if(s[j]!='H')
                 continue;
              if(s[j]=='H')
             {
                 for(z=j+1;s[z]!='';z++)
                 {
                     if(s[z]!='N')
                       continue;
                     if(s[z]=='N')
                       count++;
                   }
              }
          }
      }
   }
   
   printf("%d",count);
  return 0;
 }
/*方法二:要查找一个字符串对吧,可以用指针来指向这个字符串,然后用for循环,或者if循环来挨个指向
比如还是要查找CCHNCHN中有几个CHN

C C H N C H N
0 1 2 3 4 5 6 

进入循环,*p指向的是第一个元素,不是0,所以往后走

紧接着是if的判断部分,第一个C,说明找到了,c=1,跳出循环,p++开始指向下一个元素,还是C,然后c变成

2了,再p++往后找,发现第三个是H,走else if 此次的ch=2,然后p++开始找第二个元素,,发现是N,然后

此时的CHN=2,再往下走,发现指向了C,此时的c变成了3,然后继续走,遇到了N,此时此刻的CH=2+3=5,最

后一个字符遇到了N,然后走CHN,这个语句,现在的CHN=2+5=7,所以就是7个CHN啦。






*/
#include<stdio.h>
int main()
{
   int c=0;
   long int ch=0;
   long long int chn=0;
   char arr[100]={0};
   scanf("%s",arr);
   char*p=arr;//用指针变量p来接收一个arr的数组名
   while(*p)//*p会指向,因为会往后移动
   {
       if(*p=='C')
         c++;
       else if(*p=='H')
         ch+=c;
       else if(*p=='N')
         chn+=ch;
      
      p++;
    }
    printf("%d",chn);   
   return 0;
  }
  



*/
#include<stdio.h>
int main()
{
   int c=0;
   long int ch=0;
   long long int chn=0;
   char arr[100]={0};
   scanf("%s",arr);
   char*p=arr;//用指针变量p来接收一个arr的数组名
   while(*p)//*p会指向,因为会往后移动
   {
       if(*p=='C')
         c++;
       else if(*p=='H')
         ch+=c;
       else if(*p=='N')
         chn+=ch;
      
      p++;
    }
    printf("%d",chn);   
   return 0;
  }
  

好啦,这就是今天的分享的题目啦,然后可能我叙述的会有一些冗杂和重复,希望大家可以耐着性子读下去,希望本篇博客对大家有所帮助,里面的思路可以帮助大家处理一些相似的问题。