TL;DR
Vertical (column-by-column) scanning β O(S) time where S is the total number of characters, O(1) extra space.
Approach 1 β Brute force: test every prefix length
Any common prefix is a prefix of the first string. So try prefixes of the first string from longest to shortest and return the first one every other string starts with.
from typing import List
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
first = strs[0]
for k in range(len(first), -1, -1):
candidate = first[:k]
if all(s.startswith(candidate) for s in strs):
return candidate
return ""
Complexity: O(mΒ² Β· n) time in the worst case (m = length of the first string, n = number of strings) β each startswith is O(m) and we may try m prefix lengths. O(m) space for the slices.
At 200Γ200 this still passes, but it rechecks the same leading characters over and over β the mismatch position can be found in a single coordinated pass.
Approach 2 β Vertical scanning
The insight: the strings agree on a prefix of length i+1 exactly when they agree on columns 0..i. So compare one column at a time and stop at the first column where any string disagrees or ends.
from typing import List
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
first = strs[0]
for i in range(len(first)):
ch = first[i]
for s in strs[1:]:
if i >= len(s) or s[i] != ch:
return first[:i]
return first
Walkthrough on strs = ["flower", "flow", "flight"]:
- Column 0:
ch = 'f' β "flow" and "flight" both have f. Continue.
- Column 1:
ch = 'l' β both others have l. Continue.
- Column 2:
ch = 'o' β "flow" has o, but "flight" has i β mismatch.
- Return
first[:2] = "fl". Matches the expected output.
Complexity: O(S) time worst case (S = sum of all string lengths), but it stops at the first mismatch, so the best case is O(n) after one column. O(1) extra space.
Approach 3 β Horizontal scanning (fold)
The insight: LCP is associative β LCP(a, b, c) = LCP(LCP(a, b), c). Keep a running prefix and shrink it against each new string; if it ever becomes empty, stop early.
from typing import List
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
prefix = strs[0]
for s in strs[1:]:
while not s.startswith(prefix):
prefix = prefix[:-1]
if not prefix:
return ""
return prefix
Walkthrough on ["flower", "flow", "flight"]: prefix = "flower"; against "flow" it shrinks "flower" β "flowe" β "flow"; against "flight" it shrinks "flow" β "flo" β "fl", which "flight" starts with. Return "fl".
Complexity: O(S) time, O(m) space for the running prefix.
Approach 4 β Sort trick: compare only the extremes
The insight: under lexicographic order, the two most different strings are the smallest and the largest. Any character shared by min(strs) and max(strs) is shared by everything ordered between them, so the LCP of the whole list equals the LCP of just those two strings.
from typing import List
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
lo, hi = min(strs), max(strs)
for i in range(len(lo)):
if lo[i] != hi[i]:
return lo[:i]
return lo
Complexity: O(S) time to find the min and max, then O(m) for one pairwise comparison; O(1) extra space. Elegant, and a nice fact to mention even though the asymptotics tie with vertical scanning.
Common pitfalls
- Forgetting that a string may end before the current column β check
i >= len(s) before reading s[i], or "flow" vs "flower" throws an IndexError instead of returning "flow".
- Returning
None or crashing on a list containing an empty string β the answer is simply "".
- In the horizontal fold, shrinking with
startswith but never checking for the empty prefix β that loops forever only if you shrink incorrectly; the if not prefix: return "" early-out also saves time on disjoint inputs like ["dog", "racecar"].
Pattern takeaway
When comparing many sequences positionally, think in columns, not rows: advance a single index while all sequences agree, and stop at the first disagreement. And remember the reduction trick β if a property is preserved under an ordering (here, lexicographic min/max bracketing everyoneβs prefixes), you may only need to examine the extremes.