1 算法题 :检查一个字符串是否是某个字符串的旋转
1.1 题目含义
检查一个字符串是否是某个字符串的旋转,这是一个常见的字符串操作问题。具体来说,这个问题要求判断给定的一个字符串是否可以通过将另一个字符串的所有字符按照相同的顺序进行循环位移(即旋转)来得到。
例如,字符串 “abcde” 是 “cdeab” 的旋转,因为可以将 “cdeab” 向左旋转两个位置得到 “abcde”。但是,字符串 “abc” 并不是 “bcd” 的旋转,因为即使尝试所有可能的旋转方式,也无法得到 “abc”。
1.2 示例
示例 1:
- 输入:s1 = “abcde”, s2 = “cdeab”
- 输出:true
- 解释:字符串 “cdeab” 可以通过向左旋转两个位置得到字符串 “abcde”。
示例 2:
- 输入:s1 = “abc”, s2 = “bcd”
- 输出:false
- 解释:无法通过旋转字符串 “bcd” 来得到字符串 “abc”。
示例 3:
- 输入:s1 = “waterbottle”, s2 = “erbottlewat”
- 输出:true
- 解释:字符串 “erbottlewat” 可以通过向右旋转一定位置得到字符串 “waterbottle”。
2 解题思路
解题思路如下:
(1)判断长度:
首先,检查两个字符串 s1 和 s2 的长度是否相等。如果长度不等,那么 s2 显然不能是 s1 的旋转,因为旋转操作不会改变字符串的长度。直接返回 false。
(2)拼接字符串:
将 s1 和 s1 拼接在一起,形成一个新的字符串 doubleS1。如果 s2 是 s1 的旋转,那么 s2 必然是 doubleS1 的子串。
(3)查找子串:
使用双指针在 doubleS1 中查找 s2。遍历 doubleS1,每次检查从当前位置开始的子串是否与 s2 相等。这可以通过逐个字符比较来实现。
(4)返回结果:
如果在 doubleS1 中找到了与 s2 相等的子串,说明 s2 是 s1 的旋转,返回 true。
如果遍历完整个 doubleS1 都没有找到匹配的子串,则返回 false。
这个解题思路的时间复杂度是 O(n),其中 n 是字符串 s1(或 s2)的长度,因为我们最多需要遍历 doubleS1 一次。空间复杂度是 O(n),因为需要存储拼接后的字符串 doubleS1。虽然这个空间复杂度不是最优的,但这种方法实现起来比较简单,并且对于大多数情况来说性能也是可接受的。如果需要进一步优化空间复杂度,可以考虑不使用额外的空间存储拼接后的字符串,但这通常会增加代码的复杂性。
3 算法实现代码
3.1 使用拼接遍历
如下为算法实现代码:
#include <iostream>
#include <string> class Solution
{
public:bool isRotation(const std::string& s1, const std::string& s2){// 检查两个字符串长度是否相等 if (s1.length() != s2.length()) {return false;}// 判断空字符串 if (s1.length() == 0) {return true;}// 拼接字符串 std::string doubleS1 = s1 + s1;// 使用双指针查找子串 size_t s2Len = s2.length();for (size_t i = 0; i < doubleS1.length() - s2Len + 1; ++i) {if (doubleS1.substr(i, s2Len) == s2) {// 找到匹配的子串,返回true return true;}}// 没有找到匹配的子串,返回false return false;}
};
这段代码定义了一个 isRotation 函数,它接受两个字符串 s1 和 s2 作为参数,并返回一个布尔值,表示 s2 是否是 s1 的旋转。
注意,此代码使用了 std::string 类的 substr 成员函数来从 doubleS1 中提取与 s2 长度相同的子串,并检查它们是否相等。这种方法简单直观,但可能不是最高效的,特别是当字符串很长时。在实际应用中,可能需要根据性能要求进行优化。
调用上面的算法,并得到输出:
int main()
{Solution s;// 示例1 std::string s1 = "abcde";std::string s2 = "cdeab";std::cout << "Is " << s2 << " a rotation of " << s1 << "? " << (s.isRotation(s1, s2) ? "Yes" : "No") << std::endl;// 示例2 s1 = "abc";s2 = "bcd";std::cout << "Is " << s2 << " a rotation of " << s1 << "? " << (s.isRotation(s1, s2) ? "Yes" : "No") << std::endl;// 示例3 s1 = "waterbottle";s2 = "erbottlewat";std::cout << "Is " << s2 << " a rotation of " << s1 << "? " << (s.isRotation(s1, s2) ? "Yes" : "No") << std::endl;return 0;
}
上面代码的输出为:
Is cdeab a rotation of abcde? Yes
Is bcd a rotation of abc? No
Is erbottlewat a rotation of waterbottle? Yes
3.2 使用数组循环
上面算法的实现,使用到了字符串拼接,占用了一定的内存空间,如果避免使用拼接字符串的方法,可以将 s1 视为一个循环数组,然后检查 s2 是否可以通过将 s1 的某个起始位置作为起点,按照数组顺序读取得到。如果找到了这样的起始位置,那么 s2 就是 s1 的旋转。
如下为算法实现代码:
#include <iostream>
#include <string>
#include <vector> class Solution
{
public:bool isRotation(const std::string& s1, const std::string& s2){// 检查两个字符串长度是否相等 if (s1.length() != s2.length()) {return false;}// 判断空字符串 if (s1.length() == 0) {return true;}int n = s1.size();for (int i = 0; i < n; ++i) {bool isMatch = true;for (int j = 0; j < n; ++j) {int index = (i + j) % n; // 循环数组的索引 if (s1[index] != s2[j]) {isMatch = false;break;}}if (isMatch) {return true; // 找到匹配的起始位置 }}return false; // 没有找到匹配的起始位置 }
};
在这个实现中,使用了两个嵌套的循环。外部循环遍历 s1 的所有可能的起始位置,内部循环则用于比较从当前起始位置开始的 s1 的子串和 s2 是否相同。内部循环使用取模运算 (i + j) % n 来实现循环数组的索引,这样就可以模拟从 s1 的不同位置开始读取字符的过程。
如果对于某个起始位置i,s1 的子串和 s2 完全相同,那么就找到了匹配的起始位置,并返回 true。如果遍历完所有可能的起始位置都没有找到匹配,那么返回 false。
这种方法的时间复杂度是 O(n^2),其中 n 是字符串的长度,因为代码中有两层循环。虽然这种方法避免了拼接字符串,但它在处理长字符串时可能不够高效。对于更高效的解决方案,可以考虑使用哈希表或其他数据结构来加速字符匹配的过程。
4 测试用例
以下是针对上面算法的测试用例,基本覆盖了各种情况:
(1)基础测试用例:
- 输入1:s1 = “abcde”
- 输入2:s2 = “cdeab”
- 预期输出:true(s2 是 s1 的旋转,因为 s1 可以通过将末尾的"de"移到开头得到 s2)
(2)完全相同字符串:
- 输入1:s1 = “abcde”
- 输入2:s2 = “abcde”
- 预期输出:true(相同的字符串可以视为自己的旋转)
(3)长度不同字符串:
- 输入1:s1 = “abcde”
- 输入2:s2 = “abcdef”
- 预期输出:false(s2 不是 s1 的旋转,因为它们的长度不同)
(4)旋转一个字符:
- 输入1:s1 = “a”
- 输入2:s2 = “a”
- 预期输出:true(一个字符的旋转是其自身)
(5)非旋转字符串:
- 输入1:s1 = “hello”
- 输入2:s2 = “olleh”
- 预期输出:false(s2 不是 s1 的旋转,因为即使移动 s1 的字符也无法得到 s2)
(6)包含重复字符的字符串:
- 输入1:s1 = “abbacaa”
- 输入2:s2 = “bacaaab”
- 预期输出:true(尽管 s1 和 s2 都包含重复字符,但 s2 仍然是 s1 的旋转)
(7)空字符串:
- 输入1:s1 = “”
- 输入2:s2 = “”
- 预期输出:true(空字符串是自身的旋转)