题目描述
给定一个字符串 s
和一个整数 k
,从字符串开头算起,每计数至 2k
个字符,就反转这 2k
字符中的前 k
个字符。
- 如果剩余字符少于
k
个,则将剩余字符全部反转 - 如果剩余字符小于
2k
但大于或等于k
个,则反转前k
个字符,其余字符保持原样
示例 1:
输入:s = "abcdefg", k = 2
输出:"bacdfeg"
示例 2:
输入:s = "abcd", k = 2
输出:"bacd"
提示:
1 <= s.length <= 10^4
s
仅由小写英文字母组成1 <= k <= 10^4
解题思路
核心思想
这题的关键是理解反转的周期性规律:
- 每
2k
个字符为一个处理周期 - 在每个周期内,只反转前
k
个字符 - 处理边界情况:最后一段可能不足
2k
个字符
算法步骤
- 将字符串转换为字符数组(便于修改)
- 以
2k
为步长遍历字符串 - 对每个起始位置,反转接下来的
k
个字符(注意边界) - 返回修改后的字符串
算法过程
s = "abcdefg", k = 2
第一轮:位置 0-3 (2k=4个字符)
原始: [a][b][c][d][e][f][g]
↑ ↑
反转前k=2个
结果: [b][a][c][d][e][f][g]
第二轮:位置 4-7 (剩余3个字符,< 2k)
当前: [b][a][c][d][e][f][g]
↑ ↑
反转前k=2个
最终: [b][a][c][d][f][e][g]
代码实现
Java 实现
class Solution {
public String reverseStr(String s, int k) {
// 转换为字符数组,便于原地修改
char[] chars = s.toCharArray();
int n = chars.length;
// 以2k为步长遍历
for (int i = 0; i < n; i += 2 * k) {
// 计算本轮需要反转的区间
int left = i;
int right = Math.min(i + k - 1, n - 1); // 注意边界
// 反转区间 [left, right]
reverse(chars, left, right);
}
return new String(chars);
}
/**
* 反转字符数组指定区间
* @param chars 字符数组
* @param left 左边界(包含)
* @param right 右边界(包含)
*/
private void reverse(char[] chars, int left, int right) {
while (left < right) {
char temp = chars[left];
chars[left] = chars[right];
chars[right] = temp;
left++;
right--;
}
}
}
C# 实现
public class Solution {
public string ReverseStr(string s, int k) {
// 转换为字符数组
char[] chars = s.ToCharArray();
int n = chars.Length;
// 以2k为步长遍历
for (int i = 0; i < n; i += 2 * k) {
// 计算本轮需要反转的区间
int left = i;
int right = Math.Min(i + k - 1, n - 1);
// 反转区间 [left, right]
Reverse(chars, left, right);
}
return new string(chars);
}
/// <summary>
/// 反转字符数组指定区间
/// </summary>
/// <param name="chars">字符数组</param>
/// <param name="left">左边界(包含)</param>
/// <param name="right">右边界(包含)</param>
private void Reverse(char[] chars, int left, int right) {
while (left < right) {
char temp = chars[left];
chars[left] = chars[right];
chars[right] = temp;
left++;
right--;
}
}
}
复杂度分析
-
时间复杂度:O(n)
- 每个字符最多被访问一次
- 反转操作总计不超过 n/2 次交换
-
空间复杂度:O(n)
- 需要额外的字符数组存储结果
- 如果允许修改原字符串,可以达到 O(1)