字符串旋转判断 — 完整知识总结
判断一个字符串是否为另一个字符串旋转之后的字符串,巧妙运用
strstr一招制胜。
一、问题描述
给定两个字符串s1和s2,判断s2是否为s1旋转之后的字符串。
示例:
| s1 | s2 | 结果 | 说明 |
|---|---|---|---|
"AABCD" | "BCDAA" | ✅ 是 | 左旋2个字符 |
"abcd" | "ACBD" | ❌ 不是 | 字符顺序不对 |
旋转说明(以"AABCD"为例):
AABCD 左旋一个字符 → ABCDA AABCD 左旋两个字符 → BCDAA AABCD 右旋一个字符 → DAABC二、核心解决方案
方法一:暴力法(逐一旋转比较)
思路:
- 获取两个字符串长度,长度不同直接返回
0 - 依次左旋
s10 到len-1个字符 - 每次旋转后与
s2比较,相同则返回1 - 所有旋转都不匹配则返回
0
#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<string.h>#defineMAX30inti=0;// 左旋字符串 k 个字符voidleft_rotate(char*str,intk){intlen=strlen(str);k%=len;/*比如k=5,len=4,k%len,k=1(左旋5个字符=左旋一个字符)*/for(inti=0;i<k;i++)//k觉得了旋转字符的个数(若是两个字符,就旋转两次,一次一个字符){chartemp=str[0];//储存第一字符//把除了第一个字符以为的其他字符往前移for(intj=0;j<len-1;j++){str[j]=str[j+1];}str[len-1]=temp;}}// 判断 s2 是否是 s1 旋转后的字符串intis_rotation(char*s1,char*s2){intlen1=strlen(s1);intlen2=strlen(s2);// 长度不同,不可能是旋转if(len1!=len2){return0;}// 逐一尝试左旋 0 到 len-1 个字符for(i=0;i<len1;i++)//i决定了字符旋转个数{chartemp[100];strcpy(temp,s1);//把temp内容改成s1left_rotate(temp,i);//传参旋转if(strcmp(temp,s2)==0)//和s2比较,==0表示temp和s2相等{return1;}}return0;//若尝试了len-1个字符后还是不相等,返回假}intmain(){chars1[MAX];chars2[MAX];printf("请输入同样长度的字符串\n");printf("第一个字符串s1:\n");scanf("%s",s1);printf("第一个字符串s2:\n");scanf("%s",s2);if(is_rotation(s1,s2)){printf("%s 是 %s 旋转后的字符串,旋转个数:%d\n",s2,s1,i);}else{printf("%s 不是 %s 旋转后的字符串\n",s2,s1);}return0;}按理来说
k %= len是没必要的,因为k的范围以控制在0~len-1的范围了,改代码只是为了程序的安全性,即使有人乱传参也不影响。
方法二:拼接 + strstr 法(⭐ 推荐)
核心原理:
如果
s2是s1旋转得到的,那么s2一定是s1 + s1的子串。
数学本质:
s1 = "abc" s1 + s1 = "abcabc" 所有旋转结果都在 "abcabc" 中: "abc"(旋转0个)→ 位置 0 "bca"(旋转1个)→ 位置 1 "cab"(旋转2个)→ 位置 2算法步骤:
① 判断两个字符串长度是否相等 → 不等则返回 0 ② 处理空字符串特殊情况 ③ 创建临时字符串 temp = s1 + s1 ④ 用 strstr 在 temp 中查找 s2 ⑤ 找到返回 1,否则返回 0三、关键函数详解
1.strlen— 获取字符串长度
size_tstrlen(constchar*str);- 功能:计算字符串长度(不包括
\0) - 返回值:字符串中字符的个数
2.strcpy— 复制字符串
char*strcpy(char*dest,constchar*src);- 功能:将
src的内容复制到dest - 返回值:
dest的指针
3.strcat— 拼接字符串
char*strcat(char*dest,constchar*src);- 功能:将
src追加到dest末尾 - 返回值:
dest的指针
4.strstr— 查找子串
char*strstr(constchar*haystack,constchar*needle);- 功能:在
haystack中查找needle首次出现的位置 - 返回值:找到 → 返回首位置指针;没找到 → 返回
NULL
四、左旋与右旋的关系
核心规律
右旋 k 个字符 = 左旋 (len - k) 个字符
示例(s1 = "AABCD",长度 = 5):
| 左旋 | 等价右旋 | 结果 |
|---|---|---|
| 左旋 0 个 | 右旋 0 个 | AABCD |
| 左旋 1 个 | 右旋 4 个 | ABCDA |
| 左旋 2 个 | 右旋 3 个 | BCDAA |
| 左旋 3 个 | 右旋 2 个 | CDAAB |
| 左旋 4 个 | 右旋 1 个 | DAABC |
实际判断时
- 不区分方向:直接用
strstr在s1 + s1中查找即可,左旋右旋都能覆盖 - 需要区分方向:逐一尝试左旋/右旋 k 个字符,或利用公式
右旋k = 左旋(len-k)
五、完整代码实现
#include<stdio.h>#include<string.h>#defineMAX30/** * 判断 s2 是否是 s1 旋转后的字符串 * 返回值: * -1:不是旋转 * >=0:旋转的次数 */introtation_count(char*s1,char*s2){intlen1=strlen(s1);intlen2=strlen(s2);if(len1!=len2){return-1;}if(len1==0){return0;}// 拼接 s1 + s1chartemp[200];strcpy(temp,s1);strcat(temp,s1);// ★ 用 strstr 找到 s2 在 temp 中的位置char*pos=strstr(temp,s2);if(pos==NULL)//当在temp找不到s2就等于空指针return-1;// 不是旋转// ★ 旋转次数 = 找到的位置索引intcount=pos-temp;//s2在temp出现的起始地址减去temp的起始地址=旋转次数returncount;}intmain(){chars1[MAX];chars2[MAX];printf("请输入同样长度的字符串\n");printf("第一个字符串s1:\n");scanf("%s",s1);printf("第一个字符串s2:\n");scanf("%s",s2);intcount=rotation_count(s1,s2);if(count>=0){printf("%s 是 %s 旋转后的字符串\n",s2,s1);printf("旋转了 %d 个字符\n",count);}else{printf("%s 不是 %s 旋转后的字符串\n",s2,s1);}return0;}六、测试用例大全
| 序号 | s1 | s2 | 预期 | 说明 |
|---|---|---|---|---|
| 1 | "AABCD" | "BCDAA" | 1 | 左旋 2 个字符 |
| 2 | "abcd" | "ACBD" | 0 | 字符顺序不同,不是旋转 |
| 3 | "AABCD" | "ABCDA" | 1 | 左旋 1 个字符 |
| 4 | "AABCD" | "DAABC" | 1 | 右旋 1 个字符 |
| 5 | "hello" | "hello" | 1 | 旋转 0 个(两个相同) |
| 6 | "abc" | "abcd" | 0 | 长度不同 |
| 7 | "abc" | "bca" | 1 | 左旋 1 个 |
| 8 | "abc" | "cab" | 1 | 左旋 2 个 |
| 9 | "abc" | "cba" | 0 | 字符相同但顺序颠倒 |
七、核心原理图解
为什么s1 + s1包含所有旋转?
以s1 = "ABCD"为例:
所有可能的旋转: ABCD(旋转 0 个)、BCDA(旋转 1 个) CDAB(旋转 2 个)、DABC(旋转 3 个) s1 + s1 = "ABCDABCD" 从索引 0 开始 → 取 4 个字符 → "ABCD" ✅ 从索引 1 开始 → 取 4 个字符 → "BCDA" ✅ 从索引 2 开始 → 取 4 个字符 → "CDAB" ✅ 从索引 3 开始 → 取 4 个字符 → "DABC" ✅🎯结论:所有旋转结果都是
s1 + s1中长度为len的连续子串。
八、函数调用流程图
main() ├── is_rotation("AABCD", "BCDAA") │ ├── strlen(s1) → 获取长度 5 │ ├── strlen(s2) → 获取长度 5(相等,继续) │ ├── strcpy(temp, s1) → temp = "AABCD" │ ├── strcat(temp, s1) → temp = "AABCDAABCD" │ └── strstr(temp, s2) → 找到,返回指针 │ └── return 1 │ └── is_rotation("abcd", "ACBD") ├── strlen(s1) → 4 ├── strlen(s2) → 4(相等,继续) ├── strcpy(temp, s1) → temp = "abcd" ├── strcat(temp, s1) → temp = "abcdabcd" └── strstr(temp, s2) → 未找到,返回 NULL └── return 0九、注意事项
| 注意事项 | 说明 |
|---|---|
| 长度检查 | 必须先检查,长度不同直接返回 0 |
| 空字符串 | 两个空字符串互为旋转 |
| 内存安全 | temp数组要足够大(至少2*len+1) |
| 大小写敏感 | strstr区分大小写,"ABC"≠"abc" |
| 方向无关 | 该方法不区分左旋还是右旋 |
十、总结
| 方面 | 要点 |
|---|---|
| 核心思想 | 旋转字符串一定是s1 + s1的子串 |
| 关键函数 | strstr查找子串 |
| 方向问题 | 不区分左旋右旋,全部覆盖 |
⚡一句话总结:把
s1拼成两倍,用strstr找s2,找到就是旋转!
谢谢大家的赞赞