最长公共前缀:逐列比对,遇异即止
公共前缀是所有串都认的开头。拿第一串当标尺一列一列比,谁先露馅就截在那一列之前。
公共前缀一定是 strs[0] 的某个前缀。纵向:扫第 j 列,任一串在此列越界或字符对不上,答案就是 strs[0][:j]。横向:先把答案设成第一串,再对每个后续串把答案削到双方的公共前缀,削空即可停。两种都从「必须是每一串的开头」推出来。时间 O(S)(S 为所有字符数),空间 O(1)。
这是 LeetCode 14. Longest Common Prefix。给你一个字符串数组 strs,写出所有串共同拥有的最长前缀。前缀必须从下标 0 开始连着取,不能从中间挖一段。没有任何公共开头就返回空串。数组为空也返回空串。
主例 strs = ["flower", "flow", "flight"]。三串开头两个字符都是 f、l,第三列分别是 o、o、i,在这里分开,答案是 "fl"。对照 ["dog", "racecar", "car"],第一列 d/r/c 就对不上,答案是 ""。再对照 ["ab", "a"]:第二列时短串已经没字符了,答案只能是 "a"。
暴力可以对每个可能的前缀长度去问「是不是每一串都有这段」,重复比较很多列。缺的不是「公共开头」这个定义,而是一次扫描里同时处理两种失败:列上字符不同,以及某一串已经走完。本文用手走主例的每一列,并说明横向缩短为什么得到同一段 "fl"。
以第一串为标尺,一列一列往下比
第一直觉是枚举长度。主例最短串是 flow,长度 4,于是试 "f"、"fl"、"flo"、"flow" 是不是三串都认。"flo" 已经被 flight 否决,前面两段却被反复确认。结果能对,每一列被问了太多次。
缺的是一根不会漏解的标尺。若公共前缀非空,它一定也是第一串的前缀——否则第一串自己就不认这段开头。所以拿 strs[0] 当尺子:只问第 0 列、第 1 列……其余每一串在这一列是不是同一个字符。尺子本身不会比真正的公共前缀更短;一旦某列失败,尺子右侧全部作废。
这就是纵向比较。外层走列 j,内层走串 i。第 j 列全员相同,才有资格看下一列。任一串在这一列越界,或 strs[i][j] != strs[0][j],立刻返回 strs[0][:j]。循环能把第一串走完,说明第一串整段都被其余串认下,答案就是 strs[0]。
用手走主例。第 0 列:f / f / f,通过。第 1 列:l / l / l,通过。第 2 列:o / o / i,flight 露馅。演示停在这一列,公共前缀截在 [0, 2),就是 "fl"。后面的 w、e、r 不必再看——它们连第二串都未必对齐,更不可能是三串共享的开头。
同一事实也可以横着削。先令 ans = "flower",拿 "flow" 去削:第四个字符起对不上,ans 变成 "flow";再拿 "flight" 去削:第三个字符 o/i 对不上,ans 变成 "fl"。再来一串就继续削,削空就可以提前停。纵向按列停,横向按串停,比的都是「当前候选还是不是每一串的前缀」,答案同为 "fl"。
为什么第一串能当标尺公共前缀是交集。交集再短,也还是第一串的前缀。用 strs[0] 当尺子不会漏掉更长的公共段,因为更长的段第一串自己都没有。
短串先走完,也要立刻截断
只检查「字符不同」会漏掉另一种失败:某一串比标尺短,第 j 列根本没有字符。公共前缀不能比最短串更长,越界和字符冲突是并列的停止条件。
对照 ["ab", "a"]。第 0 列 a/a,通过。第 1 列标尺要看 b,第二串长度为 1,已经没字符了。演示停在这一列:返回 strs[0][:1],也就是 "a"。写成 if j >= len(strs[i]) || strs[i][j] != strs[0][j],越界必须写在读字符前面,否则会踩空。
空串在数组里时,第 0 列立刻越界,答案是 ""。只有一串时,没有人能否定它,答案就是它自己。这些边界都落在同一句判断上,不必另开一条路。
Go:逐列扫描
func longestCommonPrefix(strs []string) string {if len(strs) == 0 { return "" }for j := 0; j < len(strs[0]); j++ {for i := 1; i < len(strs); i++ {if j >= len(strs[i]) || strs[i][j] != strs[0][j] {return strs[0][:j]}}}return strs[0]}
1空数组没有标尺,直接返回空串。只有一串时内层循环不跑,最后返回 strs[0]。
2外层 j 走标尺的每一列。能走到这里,说明前 j 列已经被所有串认下。
3内层从第二串比起。j >= len(strs[i]) 必须写在读 strs[i][j] 前面。
4越界或对不上,公共部分刚好是标尺的前 j 个字符,主例 j=2 时切出 "fl"。
5标尺每一列都过了,说明第一串整段都是公共前缀。
总结
公共前缀必是第一串的前缀。逐列比到分歧或越界,主例在 o/o/i 切开,得到 fl。
- 跳着取公共字符、找最长公共子串,都不是这题。必须从下标 0 连着取。
- 纵向按列停,横向按串把候选削短,比的是同一件事。主例两种走法都得到 "fl"。
- 停止条件有两个:字符不同,以及短串越界。["ab","a"] 死在第二列越界,不是死在字符冲突。