01高频字符串面试题汇总
以下题目是近年来互联网大厂(字节、阿里、腾讯、美团)面试中出现频率最高的字符串相关题目。掌握这些题目,能覆盖80%的基础算法考察。
| 题目名称 | 难度 | 核心考点 | 出现频率 |
|---|---|---|---|
| 有效的括号 | Easy | 栈 (Stack) | ⭐⭐⭐⭐⭐ |
| 最长回文子串 | Medium | 动态规划 / 中心扩展 | ⭐⭐⭐⭐⭐ |
| 字符串转换整数 (atoi) | Medium | 状态机 / 边界处理 | ⭐⭐⭐⭐ |
| 实现 strStr() | Easy | KMP算法 / 双指针 | ⭐⭐⭐⭐ |
| 最长公共前缀 | Easy | 横向扫描 / 分治 | ⭐⭐⭐ |
| 反转字符串中的单词 | Medium | 双指针 / 原地修改 | ⭐⭐⭐ |
| 压缩字符串 | Easy | 双指针 / 计数 | ⭐⭐⭐ |
| 无重复字符的最长子串 | Medium | 滑动窗口 | ⭐⭐⭐⭐⭐ |
02核心算法深度解析
针对高频考点,我们整理了三种最核心的解题思路。请切换下方选项卡查看详细逻辑与代码示例。
滑动窗口 (Sliding Window)
滑动窗口是解决字符串子串问题的利器。其核心思想是维护一个窗口 [left, right],通过移动左右边界来扩大或缩小窗口,从而满足特定条件(如“无重复字符”)。
经典案例:无重复字符的最长子串
题目描述:给定一个字符串 s,请你找出其中不含有重复字符的 最长子串 的长度。
解题思路:
- 使用哈希表
Map或数组记录字符最后出现的位置。 - 右指针
right不断向右移动,将字符加入窗口。 - 如果遇到重复字符,左指针
left直接跳到重复字符上次出现位置的下一位(取最大值,防止回退)。 - 每次移动后更新最大长度
max_len = max(max_len, right - left + 1)。
Java 代码示例:
public int lengthOfLongestSubstring(String s) {
int n = s.length();
int ans = 0;
// 存储字符最后出现的索引
Map map = new HashMap<>();
for (int right = 0, left = 0; right < n; right++) {
char c = s.charAt(right);
// 如果字符已在窗口中,移动左指针
if (map.containsKey(c)) {
left = Math.max(left, map.get(c) + 1);
}
// 更新最大长度
ans = Math.max(ans, right - left + 1);
// 更新字符最后出现的位置
map.put(c, right);
}
return ans;
}
双指针 (Two Pointers)
双指针在字符串处理中应用极广,包括原地修改、查找子串、反转字符串等。主要分为“同向双指针”(如滑动窗口)和“相向双指针”(如二分查找、回文判断)。
经典案例:反转字符串中的单词 III
题目描述:给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。
解题思路:
- 将字符串转换为字符数组(因为Java/Python中字符串不可变)。
- 使用两个指针
i和j,i指向单词起始位置,j寻找单词结束位置(空格)。 - 对每个单词区间 [i, j) 进行反转。
- 更新
i = j + 1,继续处理下一个单词。
Python 代码示例:
def reverseWords(s: str) -> str:
words = s.split(' ')
reversed_words = [word[::-1] for word in words]
return ' '.join(reversed_words)
或者使用双指针原地操作
def reverseWordsInPlace(s: str) -> str:
chars = list(s)
start = 0
for end in range(len(chars) + 1):
if end == len(chars) or chars[end] == ' ':
# 反转区间 [start, end)
left, right = start, end - 1
while left < right:
chars[left], chars[right] = chars[right], chars[left]
left += 1
right -= 1
start = end + 1
return ''.join(chars)
动态规划 (Dynamic Programming)
当字符串问题涉及“最长”、“最短”、“是否匹配”且子问题有重叠时,动态规划是首选。关键在于定义状态转移方程。
经典案例:最长回文子串
题目描述:给定一个字符串 s,找到 s 中最长的回文子串。
解题思路(中心扩展法 - 优化版DP思路):
虽然纯DP需要 O(N^2) 空间,但我们可以利用回文的对称性。以每个字符(或两个字符之间)为中心,向两边扩展,直到字符不相等。这种方法时间复杂度 O(N^2),空间复杂度 O(1)。
C++ 代码示例:
string longestPalindrome(string s) {
if (s.empty()) return "";
int start = 0, end = 0;
for (int i = 0; i < s.length(); i++) {
int len1 = expandAroundCenter(s, i, i); // 奇数长度
int len2 = expandAroundCenter(s, i, i + 1); // 偶数长度
int len = max(len1, len2);
if (len > end - start) {
start = i - (len - 1) / 2;
end = i + len / 2;
}
}
return s.substr(start, end - start + 1);
}
int expandAroundCenter(const string& s, int left, int right) {
while (left >= 0 && right < s.length() && s[left] == s[right]) {
left--;
right++;
}
return right - left - 1;
}
03面试官在考察什么?
在字符串面试题中,面试官往往不仅关注结果,更关注你的编码规范和对边界条件的处理能力。以下是高分答题策略。
1. 边界条件检查
关键点:空字符串、null值、单字符字符串、全相同字符、超长字符串。
示例:在实现 atoi 时,必须处理前导空格、正负号、溢出情况(INT_MAX/INT_MIN)。如果忽略这些,代码在工程上是不可用的。
2. 空间复杂度优化
关键点:是否允许修改原字符串?是否可以使用额外空间?
示例:在“原地反转字符串”或“移除指定字符”时,尽量使用 O(1) 额外空间,通过双指针交换或覆盖来实现,这体现了你对内存管理的敏感度。
3. 时间复杂度分析
关键点:暴力解法通常是 O(N^2) 或 O(N^3),面试中通常需要优化至 O(N) 或 O(N log N)。
示例:对于“最长回文子串”,暴力枚举所有子串并判断是 O(N^3)。使用中心扩展或Manacher算法可优化至 O(N) 或 O(N^2) 但常数更小。
4. 语言特性利用
关键点:不同语言对字符串的处理机制不同。
示例:Java中 String 不可变,频繁拼接应使用 StringBuilder;Python中 split() 和 join() 非常高效;C++中 string_view 可避免拷贝。展示你对语言底层的理解能加分。
05字符串算法学习路径
建议按照以下阶段进行系统复习,逐步攻克字符串难题。
字符串基本操作与双指针
熟练掌握各语言的字符串API(截取、替换、分割、拼接)。理解双指针思想,完成“反转字符串”、“移除元素”、“三数之和”等入门题。建立对 O(N) 复杂度的直观感受。
栈、队列与哈希表
学习使用栈解决“有效括号”、“最小栈”问题。利用哈希表优化查找,解决“字母异位词”、“无重复字符的最长子串”。这是面试中最常考的组合。
动态规划与贪心
攻克“最长公共子序列”、“编辑距离”、“最长回文子串”等DP难题。理解状态定义与转移方程。同时学习贪心算法在“跳跃游戏”等字符串相关场景的应用。
KMP、Trie树与后缀数组
深入理解KMP算法的 next 数组推导。学习Trie树(前缀树)用于单词查找和自动补全。了解后缀数组/后缀自动机用于高级文本处理(虽面试较少,但属于加分项)。