返回
剑指Offer 58:左旋转字符串
Android
2023-10-09 02:27:21
揭秘「左旋转字符串」的奇妙技巧
引言
在剑指 Offer 的编程练习中,字符串操作问题层出不穷,「左旋转字符串」就是其中颇具挑战性的一道。这道题看似简单,却蕴含着巧妙的技巧,需要我们深入理解字符串的本质。
任务目标
给定一个字符串 s
和一个正整数 n
,我们的目标是将 s
向左旋转 n
个位置。
例如:
- 输入:
s
= "abcdefg",n
= 2 - 输出:"cdefgab"
要求:
s
由英文字母组成,长度在 1 到 10000 之间。n
是一个正整数,范围为 1 到 10000。
解决思路:巧用字符串拼接和切片
由于 Java 中字符串是不可变类型,因此无法直接修改原字符串。为了解决这个问题,我们可以借助字符串拼接和切片操作。
- 复制字符串: 将字符串
s
复制两次,得到新的字符串t
=s
+s
。 - 截取子字符串: 从索引
n
开始,截取字符串t
的子字符串,得到sub
=t.substring(n)
。 - 拼接字符串: 从索引 0 到
n
-1,截取字符串t
的子字符串,得到pref
=t.substring(0, n)
。 - 返回结果: 将
sub
和pref
拼接起来,得到旋转后的字符串:sub
+pref
。
代码实现
public String leftRotateString(String s, int n) {
if (s == null || s.length() == 0) {
return "";
}
int len = s.length();
n %= len; // 避免 n 过大
String t = s + s;
String sub = t.substring(n);
String pref = t.substring(0, n);
return sub + pref;
}
优化技巧:字符数组翻转
为了进一步优化代码性能,我们可以利用 Java 字符数组的特性。
优化后的代码
public String leftRotateString(String s, int n) {
if (s == null || s.length() == 0) {
return "";
}
int len = s.length();
n %= len; // 避免 n 过大
char[] chars = s.toCharArray();
reverse(chars, 0, len - 1);
reverse(chars, 0, n - 1);
reverse(chars, n, len - 1);
return new String(chars);
}
private void reverse(char[] chars, int start, int end) {
while (start < end) {
char temp = chars[start];
chars[start] = chars[end];
chars[end] = temp;
start++;
end--;
}
}
优点: 直接操作字符数组,避免了字符串拼接的额外内存开销,大大提升了代码效率。
常见问题解答**
-
如何处理
n
大于字符串长度的情况?- 代码中使用了取余运算
n %= len
,避免n
过大。这意味着当n
大于字符串长度时,只旋转字符串的部分长度。
- 代码中使用了取余运算
-
为什么需要复制字符串两次?
- 复制字符串是为了避免修改原字符串。因为 Java 中字符串是不可变类型,我们只能通过拼接和切片操作来得到新的字符串。
-
字符数组翻转是如何工作的?
- 字符数组翻转通过交换数组中元素的位置来实现。
reverse
方法将数组划分为三个部分,并依次翻转它们。
- 字符数组翻转通过交换数组中元素的位置来实现。
-
为什么字符数组翻转更有效率?
- 字符数组翻转避免了字符串拼接操作,减少了内存开销和时间复杂度。
-
如何确定旋转的次数?
- 取余运算
n %= len
确保n
不会超过字符串长度,从而避免重复旋转。
- 取余运算
总结**
「左旋转字符串」是一道看似简单却颇具技巧性的题目,考验我们对字符串操作的理解。通过巧用字符串拼接和切片,或者字符数组翻转等优化技巧,我们可以高效地解决这个问题。在实际编码过程中,应根据具体情况选择最优化的实现方式,充分发挥 Java 语言的优势。