news 2026/10/1 2:44:48

doocs/leetcode 题解实战:面试题 01.09 字符串轮转(String Rotation)的双字符串拼接判定法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
doocs/leetcode 题解实战:面试题 01.09 字符串轮转(String Rotation)的双字符串拼接判定法
  • 示例工程
  • 教程

【免费下载链接】leetcode

🔥LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解

项目地址:https://gitcode.com/doocs/leetcode
点击查看免费下载

本篇技术指南以开源仓库 doocs/leetcode 中《程序员面试金典(第 6 版)》题解目录 lcci/01.09.String Rotation 的英文题解 README_EN.md 为骨架,系统讲解"字符串轮转(String Rotation)"这一经典字符串问题的判定思路、数学原理与多语言实现。读完本文,你将掌握"长度前置判定 + 双字符串拼接 + 一次子串检查"的 O(n) 解法,并能在 Python、Java、C++、Go、TypeScript、Rust、Swift 中直接写出可运行的答案。

一、题目概述:什么是字符串轮转

根据 README_EN.md 的题目描述,本问题要求给定两个字符串s1和s2,编写代码检查s2是否为s1旋转(rotation)后的结果。所谓"旋转",指将字符串的某个前缀整体移动到末尾后形成的新串。

示例 1(成立):

Input: s1 = "waterbottle", s2 = "erbottlewat" Output: True

"waterbottle"从位置 6 处切割,把前缀"wat"挪到末尾即得到"erbottlewat"。

示例 2(不成立):

Input: s1 = "aa", s2 = "aba" Output: False

"aba"不是"aa"的任何旋转结果,且两者长度不相等。

约束条件:

  • 字符串长度范围:0 <= s1.length, s2.length <= 100000
  • 题目额外设问:能否只调用一次"检查子串"的方法就完成判定?这正是本节要解决的核心挑战。

该题对应《程序员面试金典(第 6 版)》第 1 章"数组与字符串"中的第 09 题,在仓库中位于 lcci/01.09.String Rotation/ 目录,官方难度标记为 Easy(简单)。

二、解法核心:长度判定 + 双字符串拼接

1. 第一重判定:长度不相等必非旋转

旋转只是把字符串内部元素重新排列位置,不会增加或减少任何字符。因此,若s1与s2长度不等,二者必然不是旋转关系,可以直接返回False。这一前置判定同时也是一个高效的剪枝:在字符串长度可高达 100000 的场景下,先比较长度能避免对大量不匹配输入执行昂贵的子串搜索。

2. 第二重判定:s1 + s1覆盖全部旋转情况

当两个字符串长度相等时,考察数学性质:将s1与自己拼接得到s1 + s1,该结果字符串必然包含s1的所有旋转形态。原因在于:s1的任意一次旋转,本质上是s1内部两个子串段 A、B 交换顺序(AB→BA),而BA恰好是AABB即(A)(B)(A)(B)中间部分B A的体现,因此总能在s1 + s1中连续找到。

于是原问题被归约为:判断s2是否为s1 + s1的子串,恰好只调用一次子串检查方法,完美满足题目设问。

原文以s1 = "aba"为例给出了直观演示:

# True s1 = "aba" s2 = "baa" s1 + s1 = "abaaba" ^^^ # False s1 = "aba" s2 = "bab" s1 + s1 = "abaaba"
  • "baa"是"abaaba"从下标 1 开始的连续子串(baa),判定成立;
  • "bab"无法在"abaaba"中连续匹配到,判定不成立。

3. 复杂度分析

设n为字符串s1的长度:

  • 时间复杂度:$O(n)$。长度比较为 $O(1)$,拼接s1 + s1为 $O(n)$,子串匹配在主流语言的标准库实现(如 Python 的in、Java 的String.contains、C++ 的std::string::find)中平均为线性复杂度;
  • 空间复杂度:$O(n)$。主要开销来自拼接出的长度为 $2n$ 的临时字符串。

三、七种语言实现:仓库源码逐一解读

仓库在 lcci/01.09.String Rotation/ 目录下为每种语言提供了独立的 Solution 文件,与 README_EN.md 中的代码块一一对应,可直接复制提交。

Python3

Solution.py:

class Solution: def isFlipedString(self, s1: str, s2: str) -> bool: return len(s1) == len(s2) and s2 in s1 * 2

Python 的in运算符对字符串执行子串包含检查;s1 * 2等价于s1 + s1。得益于and的短路求值,长度不等时不会执行拼接操作。

Java

Solution.java:

class Solution { public boolean isFlipedString(String s1, String s2) { return s1.length() == s2.length() && (s1 + s1).contains(s2); } }

String.contains(CharSequence)内部基于indexOf实现子串查找。

C++

Solution.cpp:

class Solution { public: bool isFlipedString(string s1, string s2) { return s1.size() == s2.size() && (s1 + s1).find(s2) != string::npos; } };

C++ 的std::string::find在未找到时返回string::npos,故以!= string::npos作为包含判定的条件。

Go

Solution.go:

func isFlipedString(s1 string, s2 string) bool { return len(s1) == len(s2) && strings.Contains(s1+s1, s2) }

Go 需显式导入标准库strings包,strings.Contains返回布尔值。

TypeScript

Solution.ts:

function isFlipedString(s1: string, s2: string): boolean { return s1.length === s2.length && (s2 + s2).indexOf(s1) !== -1; }

注意 TypeScript 版本的对称写法:拼接的是s2 + s2,检查s1是否为其子串。因为"旋转"是相互的——若s2是s1的旋转,则s1也必然是s2的旋转,所以(s2 + s2).indexOf(s1)与(s1 + s1).indexOf(s2)判定结果完全一致,这在实现层面进一步印证了拼接法的等价性。

Rust

Solution.rs:

impl Solution { pub fn is_fliped_string(s1: String, s2: String) -> bool { s1.len() == s2.len() && (s2.clone() + &s2).contains(&s1) } }

Rust 的String::contains接受模式参数;由于拼接表达式需要移动/借用所有权,这里对s2进行了clone,同样采用了"以s2为拼接基准、检查s1"的对称写法。

Swift

Solution.swift:

class Solution { func isFlippedString(_ s1: String, _ s2: String) -> Bool { return (s1.isEmpty && s2.isEmpty) || (s1.count == s2.count && (s1 + s1).contains(s2)) } }

Swift 版本额外处理了两个空字符串均为空的边界情形:s1 = ""、s2 = ""时,空串是空串的旋转,应返回True;若不处理,(s1 + s1).contains(s2)本身对两个空串也成立,但显式写出该分支让语义更清晰,避免count比较或空串拼接带来的边界歧义。

四、边界条件与易错点分析

结合题目约束(长度可达 100000)和 Swift 版本的特判,实战中需重点关注以下场景:

  1. 长度不等:如s1 = "aa"、s2 = "aba",直接返回False——这是最容易被忽视也最重要的剪枝;
  2. 两个空字符串:s1 = s2 = ""应视为旋转成立;
  3. 单个字符:s1 = "a"、s2 = "a",拼接后"aa"包含"a",成立;s1 = "a"、s2 = "b",长度相等但拼接不包含,不成立;
  4. 相同字符串:s2 == s1时(旋转 0 次),s1 + s1必包含s1,成立;
  5. 性能考量:长度上限为 100000,拼接产生的临时字符串长度为 200000,空间开销 O(n) 在题目约束下完全可接受;and/&&短路求值确保长度不等时不会执行昂贵的拼接与搜索。

五、小结

面试题 01.09 字符串轮转的核心结论可浓缩为三步:

  1. 比较长度:不等则直接返回False;
  2. 拼接字符串:构造s1 + s1(或对称地构造s2 + s2);
  3. 一次子串检查:判断s2是否包含于s1 + s1中,恰好只调用一次子串匹配方法,命中题目设问。

该解法时间复杂度 $O(n)$、空间复杂度 $O(n)$,是此类"旋转/循环位移"问题的通用范式——凡是"判断 B 是否由 A 旋转得到"的问题,都可以通过"拼接自身 + 子串包含"来归约。完整题解与多语言实现请查阅仓库 lcci/01.09.String Rotation/README_EN.md 及同目录下的各 Solution 文件,并可在 lcci/README_EN.md 中找到《程序员面试金典(第 6 版)》的全部题解索引。

  • 示例工程
  • 教程

【免费下载链接】leetcode

🔥LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解

项目地址:https://gitcode.com/doocs/leetcode
点击查看免费下载
上一篇:Heptio Ark(Velero 前身)v0.6.0 实战指南:Kubernetes 灾备、集群迁移与备份恢复全流程解析
下一篇:Easy-Vibe 实战指南:Git 与 GitHub 工作流——安装配置、SSH 认证与 AI 助手协作

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 2:44:07

翻越栏杆行为识别数据集:YOLO训练实战与避坑指南

简介&#xff1a;这份翻越栏杆行为识别数据集面向从事目标检测与行为识别的算法工程师、研究生及深度学习学习者&#xff0c;用于训练和验证YOLO系列、Faster R-CNN、SSD等模型对跨越栏杆这一危险行为的检测能力&#xff0c;可服务于安防监控、智能交通等场景。资源包共1539个文…

作者头像 李华
网站建设 2026/10/1 2:43:06

【Linux笔记】冯诺依曼体系结构

1.冯诺依曼体系结构冯诺依曼体系结构图1.1 五大核心组件说明A. 输入设备&#xff1a;键盘、磁盘、鼠标、摄像头、网卡 ... ...B. 输出设备&#xff1a;磁盘、显示器、网卡、打印机外设 输入设备 输出设备C. 存储器&#xff1a;本质是内存&#xff0c;用来存放程序指令和数据D…

作者头像 李华
网站建设 2026/10/1 2:41:41

PDF修复间:如何批量生成书签、解除限制、统一页面尺寸一次搞定

PDF修复间&#xff1a;如何批量生成书签、解除限制、统一页面尺寸一次搞定 【免费下载链接】PDFPatcher PDF补丁丁——PDF工具箱&#xff0c;可以编辑书签、剪裁旋转页面、解除限制、提取或合并文档&#xff0c;探查文档结构&#xff0c;提取图片、转成图片等等 项目地址: ht…

作者头像 李华
网站建设 2026/10/1 2:41:33

python-day07-模块

目录 一、什么叫模块&#xff1f; 二、常用的内置模块 2.1.数学计算模块-math 2.2.日期时间模块-datetime 1&#xff09;datetime类 2&#xff09;date类 3&#xff09;time类 4&#xff09;计算时间跨度-timedelta 5&#xff09;字符串时间转换-strftime、strptime 2.3.正则表…

作者头像 李华
网站建设 2026/10/1 2:40:07

倩女幽魂大盗宝藏数值推演系统原理与实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华