14. 最长公共前缀(Longest Common Prefix)
频次 ★★★ · 难度 🟢 · 高频:字节/腾讯
题目
编写一个函数返回字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 ""。
示例:
输入: ["flower","flow","flight"]
输出: "fl"
输入: ["dog","racecar","car"]
输出: ""
思路
横向扫描:先取第一个字符串作为公共前缀,然后依次与后面的每个字符串比对,每轮缩短公共前缀,直到匹配或为空。
也可以用纵向扫描:同时比较所有字符串的第 i 个字符,如果全部相同则加入结果,否则停止。纵向扫描在输入较大时可能提前退出,但两者时间效率接近。
代码
public String longestCommonPrefix(String[] strs) {
if (strs == null || strs.length == 0) return "";
String prefix = strs[0];
for (int i = 1; i < strs.length; i++) {
while (strs[i].indexOf(prefix) != 0) {
prefix = prefix.substring(0, prefix.length() - 1);
if (prefix.isEmpty()) return "";
}
}
return prefix;
}复杂度
- 时间:O(S) — S 为所有字符串的总字符数
- 空间:O(1)
边界条件
- 空数组:直接返回
""。 - 只有一个字符串:返回该字符串本身。
- 第一个字符串为空串:首轮
prefix即为"",后续循环indexOf("") == 0恒成立,最终返回""。
变式
- 纵向扫描:固定列索引 i,对每行的第 i 位比较,遇到越界或不相等就返回前 i 位结果。适合短公共前缀或输入非常大的场景,可以提前终止。
- 分治法:将数组分成左右两半,分别求 LCP,再合并(
lcp(left, right))。时间复杂度相同,但代码更复杂。 - Trie 树:将所有字符串插入 Trie 树,从根节点往下走,直到遇到分支或结束标记。适合多批次查询相同的字符串集合。
易错点
indexOf(prefix) != 0判断的是前缀是否匹配开头,不是contains或startsWith(Java 中可用startsWith代替,更直观)。substring每次生成新字符串,频繁截断在字符串很长时会有 O(L²) 的性能损耗(但本题字符串长度通常不大)。- 不要忘记
strs == null的判空。
面试追问
- 如果只给两个很长的字符串,求最长公共前缀怎么办? 直接用
while+indexOf截断法效率足够;也可以转换为”两字符串逐位比较”(纵向),不需要额外空间。 - Trie 树方案有什么优缺点? 优点是建树后任意新字符串求 LCP 只需 O(L) 匹配;缺点是建树本身 O(S) 时间和 O(S) 空间,且只适合多查询场景。
关联题
- 同套路:分治法 LCP 是归并思想在字符串上的应用
- 进阶:208. 实现 Trie —— 前缀匹配的经典数据结构
- 基础:字符串匹配基本功见数组与字符串