[刷题]判断旋转字符串

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第12天,点击查看活动详情

前言

       本文是关于刷题——判断旋转字符串的C语言实现。

       新手一个,水平比较低,还请包涵。

判断旋转字符串

题目描述

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

例如:给定s1 = AABCD和s2 = BCDAA,返回1

给定s1 = abcd和s2 = ACBD,返回0

AABCD左旋一个字符得到ABCDA

AABCD左旋两个字符得到BCDAA

AABCD右旋一个字符得到DAABC

思路分析

双指针移位法

       设定两个指针,一个头指针str_head,一个尾指针str_end,

       左旋时:把str_head指向的值丢到临时变量tmp去,然后第一个往后的字符串整体左移一位,再把tmp的值放到str_end指向的位置;

左旋字符串图解.png

       右旋时:把str_end指向的值丢到临时变量tmp去,然后第一个往后的字符串整体左移一位,再把tmp的值放到str_head指向的位置。

右旋字符串图解.png

单字符旋转判断

       写一个判断函数JugRota(),其中调用一个旋转字符串函数RotaStr(),RotaStr()既可以左旋又可以右旋字符串,用的是双指针移位法。

       输入两个字符串str和_str,判断第一个是不是第二个旋转之后得到的,在JugRota()中把第二个字符串放入一个临时字符数组tmp中,先判断是否相同,再判断是否是左旋得到的,再判断是否是右旋得到的,都不是的话就是不能旋转得到的。

       判断左右旋的方法是把tmp中字符串左旋或右旋后马上和str字符串比较看看是否相同,不是的话继续旋转,这里设置次数为sz/2,sz是字符串长度。

       为什么次数这样设置呢?

       其实在思考过程中会发现左旋和右旋其实是互为逆过程的,这样的话左旋得到的总可以使用右旋得到,比如ABBCD左旋一个字符得到BBCDA,而BBCDA又可以由ABBCD右旋四个字符得到,那怎么区分左右旋呢?我这里就以中间为界限,每次判断时最多左旋或者右旋sz/2次,如果还不相同就表示是不由该旋转方向得到的。

代码实现

//旋转字符串的函数,每次调用只旋转一次
void RotaStr(char *str, char dir)
{
	int i = 0;
	int j = 0;
	char tmp = 0;
	int sz = strlen(str);
	char* str_head = str;//头指针
	char* str_end = str + sz - 1;//尾指针

	if ('L' == dir)//左旋
	{
		tmp = *str_head;
		for (j = 0; j < sz - 1; j++)
		{
			*(str + j) = *(str + j + 1);
		}
		*str_end = tmp;
	}
	else if ('R' == dir)//右旋
	{
		tmp = *str_end;
		for (j = 0; j < sz - 1; j++)
		{
			*(str_end - j) = *(str_end - j - 1);
		}
		*str_head = tmp;
	}
	else
	{
		printf("旋向输入有误!\n");
	}
}


int JugRota(char *str, char *_str, int sz)
{
	int i = 0;
	//用临时字符数组替代_str进行旋转
	char tmp[20] = {0};
	strcpy(tmp, _str);
	//判断是否相同
	if (!strcmp(str, tmp))
		return -1;
	//判断是否是左旋得到
	for (i = 0; i < sz / 2; i++)
	{
		RotaStr(tmp, 'L');
		if (!strcmp(str, tmp))
			return 1;
	}
	
	strcpy(tmp, _str);//刷新临时字符数组
	//判断是否是右旋得到
	for (i = 0; i < sz / 2; i++)
	{
		RotaStr(tmp, 'R');
		if (!strcmp(str, tmp))
			return 0;
	}
	//都不是的话就是不能旋转得到
	return -2;
}

int main()
{
	char str[20] = { 0 };
	char _str[20] = { 0 };
	printf("请分别输入要判断的两串字符串:\n");
	scanf("%s %s", str, _str);
	
	if (1 == JugRota(str, _str, strlen(str)))
	{
		printf("第一串字符串是由第二串字符串左旋得到的!\n");
	}
	else if(0 == JugRota(str, _str, strlen(str)))
	{
		printf("第一串字符串是由第二串字符串右旋得到的!\n");
	}
	else if(-1 == JugRota(str, _str, strlen(str)))
	{
		printf("第一串字符串与第二串字符串相同!\n");
	}
	else
	{
		printf("第一串字符串与第二串字符串不存在旋转互化关系!\n");
	}
	return 0;
}

更简便的做法

       实际上该题只要求判断是不是旋转得到的而没有特意区分左右旋,同时左右旋可能等效,所以只考虑旋转的话这题有一个很简单的做法:

       其实ABCDE无论怎么旋,旋转后的所有结果,都包含在了ABCDEABCD这个字符串里了。

       所以做法很简单,只需要将原字符串再来一遍接在后面,然后找一找待查找的字符串是不是两倍原字符串的子集即可。

       主要用到几个库函数,同时要注意先判断两字符串长度相不相等。

int JugRota(const char * src, char * find)
{
	if(strlen(src) != strlen(find))
		return 0;
	char tmp[256] = { 0 }; //用一个辅助空间将原字符串做成两倍原字符串
	strcpy(tmp, src); //先拷贝一遍
	strcat(tmp, src); //再连接一遍
	return strstr(tmp, find) != NULL; //看看找不找得到
}