一、前言
字符串是编程中最常用的数据类型之一,而反转、子串截取、字符计数、回文判断是字符串的核心基础操作,也是面试高频考点。本文将用 3 小时精准拆解这些知识点,结合 3 道 LeetCode 经典题目(Easy+Easy+Medium),从理论到实战帮你彻底掌握,适合入门进阶和面试复盘。
二、0-30 分钟:字符串核心知识点精讲
2.1 字符串本质与存储特性
- 字符串是字符的有序序列,底层在 Java 中以char[]数组实现(不可变特性:String 类被 final 修饰,修改时会创建新对象);
- 关键特性:有序性(支持索引访问)、不可变性(需注意性能开销)、可比较性(equals ()/compareTo ())。
2.2 三大基础操作
2.2.1 字符串反转
- 核心思路:
- 双指针法(原地修改,适合字符数组):左右指针分别从首尾向中间移动,交换元素;
- 辅助容器法(适合不可变字符串):遍历原字符串,从后向前存入新字符串 / StringBuilder。
- 时间复杂度:O (n)(需遍历所有字符),空间复杂度:O (1)(双指针原地)/ O (n)(辅助容器)。
2.2.2 子串截取
- Java 关键 API:String substring(int beginIndex)(从 beginIndex 截取到末尾)、String substring(int beginIndex, int endIndex)(左闭右开,[begin, end));
- 注意事项:索引越界会抛出StringIndexOutOfBoundsException,endIndex 超出字符串长度时默认截取到末尾。
2.2.3 字符计数
- 核心思路:
- 数组统计(适合 ASCII 字符):用大小为 256 的 int 数组记录每个字符的出现次数;
- 哈希表统计(适合 Unicode 字符):用HashMap<Character, Integer>存储字符与计数的映射;
- 字符串 API 辅助:String.chars()流式统计(Java 8+)。
2.3 回文判断核心逻辑
- 回文定义:正读和反读完全相同的字符串(如 "abcba"、"A man, a plan, a canal: Panama");
- 核心判断思路:
- 预处理:过滤非字母数字字符(保留 a-z、A-Z、0-9),统一大小写(避免大小写干扰);
- 验证:双指针从首尾向中间对比,所有字符一致则为回文。
- 延伸:回文子串 / 子序列问题(需关注 “连续” 与 “非连续” 的区别)。
三、30-110 分钟:刷题实战(3 道经典题)
3.1 Easy:LeetCode344 反转字符串
题目描述
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O (1) 的额外空间解决这一问题。
解题思路
- 核心:利用字符串的有序性,双指针原地交换;
- 步骤:
- 定义左指针 left = 0,右指针 right = s.length – 1;
- 循环条件:left < right;
- 交换 s[left] 和 s[right],然后 left++、right–,直到指针相遇。
Java 代码(高亮版)
java
运行
class Solution {
public void reverseString(char[] s) {
// 双指针初始化
int left = 0;
int right = s.length – 1;
// 循环交换,直到指针相遇
while (left < right) {
// 临时变量保存左指针字符
char temp = s[left];
s[left] = s[right];
s[right] = temp;
// 指针移动
left++;
right–;
}
}
}
复杂度分析
- 时间复杂度:O (n),n 为字符数组长度,需遍历一半字符(O (n/2) = O (n));
- 空间复杂度:O (1),仅使用常数级额外空间(临时变量 temp),满足原地修改要求。
注意事项
- 边界处理:当数组为空(length=0)或长度为 1 时,无需交换,直接返回;
- 无需额外数组:题目禁止分配新数组,双指针是最优解法;
- 字符数组特性:Java 中 char [] 是可变的,可直接通过索引修改元素。
3.2 Easy:LeetCode125 验证回文串
题目描述
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样,则可以认为该短语是一个回文串。字母和数字都属于字母数字字符。给你一个字符串 s,请你验证它是否是回文串。
解题思路
- 核心:先预处理字符串(过滤 + 大小写转换),再用双指针验证回文;
- 步骤:
- 预处理:遍历字符串,保留字母数字字符,统一转为小写;
- 双指针验证:左指针从 0 开始,右指针从预处理后字符串末尾开始,逐一对比字符是否一致;
- 若所有对比都一致则为回文,否则不是。
Java 代码(高亮版)
java
运行
class Solution {
public boolean isPalindrome(String s) {
// 步骤1:预处理字符串(过滤非字母数字 + 转小写)
StringBuilder sb = new StringBuilder();
for (char c : s.toCharArray()) {
//toCharArray() 是 Java 中 String 类的内置方法,核心作用是:将当前字符串对
象,转换为一个对应的 char 类型数组
// 判断是否为字母或数字
if (Character.isLetterOrDigit(c)) {
// 转为小写并加入StringBuilder
sb.append(Character.toLowerCase(c));
}
}
// 预处理后的字符串
String processed = sb.toString();
//将可变的 StringBuilder 对象中存储的字符序列,转换为一个标准的、
不可变的 String 字符串对象
// 步骤2:双指针验证回文
int left = 0;
int right = processed.length() – 1;
while (left < right) {
// 对比左右指针字符
if (processed.charAt(left) != processed.charAt(right)) {
return false;
}
left++;
right–;
}
return true;
}
}
优化版代码(无额外 StringBuilder,双指针直接处理原字符串)
java
运行
class Solution {
public boolean isPalindrome(String s) {
int left = 0;
int right = s.length() – 1;
while (left < right) {
// 左指针跳过非字母数字字符
while (left < right && !Character.isLetterOrDigit(s.charAt(left))) {
left++;
}
// 右指针跳过非字母数字字符
while (left < right && !Character.isLetterOrDigit(s.charAt(right))) {
right–;
}
// 对比(转小写后)
if (Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right))) {
return false;
}
left++;
right–;
}
return true;
}
}
复杂度分析
- 原始版:时间复杂度 O (n)(遍历 2 次字符串:预处理 + 验证),空间复杂度 O (n)(StringBuilder 存储预处理结果);
- 优化版:时间复杂度 O (n)(仅遍历 1 次字符串),空间复杂度 O (1)(无额外容器),更优。
注意事项
- 非字母数字字符:包括空格、标点、符号等,需完全过滤;
- 大小写无关:如 "A" 和 "a" 视为相同,必须统一转换;
- 空字符串 / 全非字母数字:预处理后为空,视为回文(返回 true)。
3.3 Medium:LeetCode5 最长回文子串
题目描述
给你一个字符串 s,找到 s 中最长的回文子串。如果字符串的长度为 1,则返回该字符串。
解题思路
- 核心:回文子串的对称性 → 中心扩展法(入门必学,时间复杂度 O (n²),空间复杂度 O (1),适合入门);
- 关键观察:
- 奇数长度回文:中心是 1 个字符(如 "abcba" 的中心是 "c");
- 偶数长度回文:中心是 2 个字符(如 "abba" 的中心是 "bb");
- 步骤:
- 遍历字符串每个字符(作为奇数中心)和每两个相邻字符(作为偶数中心);
- 对每个中心,向左右扩展,记录最大回文子串的起始和结束索引;
- 遍历结束后,截取最长回文子串返回。
Java 代码(高亮版)
java
运行
class Solution {
// 记录最长回文子串的起始索引和长度(避免每次截取字符串,提升效率)
private int start = 0;
private int maxLen = 1;
public String longestPalindrome(String s) {
int n = s.length();
// 边界条件:字符串长度<=1,直接返回
if (n <= 1) {
return s;
}
// 遍历每个可能的中心,扩展回文
for (int i = 0; i < n; i++) {
expandAroundCenter(s, i, i); // 奇数长度回文(中心1个字符)
expandAroundCenter(s, i, i+1); // 偶数长度回文(中心2个字符)
}
// 截取最长回文子串(start到start+maxLen)
return s.substring(start, start + maxLen);
}
// 中心扩展核心方法:left和right为初始中心
private void expandAroundCenter(String s, int left, int right) {
int n = s.length();
// 向左右扩展,直到字符不相等或越界
while (left >= 0 && right < n && s.charAt(left) == s.charAt(right)) {
left–;
right++;
}
// 计算当前回文子串的长度(right – left – 1,因为退出循环时left和right已越界/不相等)
int currentLen = right – left – 1;
// 更新最长回文子串的起始索引和长度
if (currentLen > maxLen) {
maxLen = currentLen;
// 起始索引:left+1(因为退出循环时left多减了1)
start = left + 1;
}
}
}
复杂度分析
- 时间复杂度:O (n²),n 为字符串长度。遍历每个中心(O (n) 个中心),每个中心最多扩展 O (n) 次,总次数 O (n²);
- 空间复杂度:O (1),仅使用常数级额外变量(start、maxLen、left、right),无额外容器。
注意事项
- 中心扩展的边界:left >= 0(左边界)、right < n(右边界),避免索引越界;
- 回文长度计算:退出循环时,left 和 right 已超出有效回文范围,实际长度为 right – left – 1;
- 起始索引更新:start = left + 1(因为 left 多减了 1),避免截取时出错;
- 其他解法:Manacher 算法(时间复杂度 O (n),进阶用),但中心扩展法更易理解和实现,适合面试入门。
-
class Solution {
public String longestPalindrome(String s) {
if (s == null || s.length() == 0) return "";// 1. 预处理字符串,统一成奇数长度
StringBuilder sb = new StringBuilder();
sb.append("^");
for (char c : s.toCharArray()) {
sb.append("#").append(c);
}
sb.append("#").append("$");
String T = sb.toString();int n = T.length();
int[] p = new int[n]; // 回文半径数组
int C = 0, R = 0; // 中心、最右边界// 记录最长回文的中心和半径
int maxCenter = 0, maxRadius = 0;// 2. 遍历处理
for (int i = 1; i < n – 1; i++) {
// 利用对称性初始化 p[i]
int mirror = 2 * C – i;
if (i < R) {
p[i] = Math.min(R – i, p[mirror]);
}// 3. 往外扩展
while (T.charAt(i + p[i] + 1) == T.charAt(i – p[i] – 1)) {
p[i]++;
}// 4. 若超过 R,更新 C 和 R
if (i + p[i] > R) {
C = i;
R = i + p[i];
}// 记录最大值
if (p[i] > maxRadius) {
maxRadius = p[i];
maxCenter = i;
}
}// 5. 还原回原字符串
int start = (maxCenter – maxRadius) / 2;
return s.substring(start, start + maxRadius);
}
}
四、总结与复盘
4.1 核心知识点回顾
4.2 解题技巧提炼
- 双指针:字符串反转、回文验证的万能工具,优先考虑(空间效率高);
- 中心扩展:解决回文子串问题的入门最优解,需兼顾奇偶中心;
- 预处理:回文问题必做步骤,避免非目标字符干扰。



