https://leetcode.com/problems/longest-common-prefix/
Given an array of strings
strs.
Return the longest common prefix string amongst an array of strings.
If there is no common prefix, return an empty string"".
-
"Can you restate the problem in your own words?"
β "We need to find the longest string that appears at the beginning of every string in the array. All strings must start with this prefix." -
"What does 'common prefix' mean exactly?"
β "A prefix is a substring that starts at index 0. A common prefix is a prefix shared by all strings. For example, 'fl' is a common prefix for ['flower', 'flow', 'flight']." -
"What should we return if there's no common prefix?"
β "Return an empty string ''." -
"What if the array has only one string?"
β "That string itself is the longest common prefix since it's the only string." -
"What if the array is empty?"
β "We should handle this edge case by returning an empty string, though the problem typically guarantees at least one string." -
"Can the strings contain special characters or just letters?"
β "The strings can contain any characters. We just need to match them character by character." -
"If one string is a prefix of another, what happens?"
β "The shorter string would be the common prefix. For example, ['flow', 'flower'] β 'flow'."
-
"What type of problem is this?"
β "This is a string comparison problem that requires finding commonality across multiple strings." -
"What approaches come to mind?"
β "Horizontal scanning (compare pairs), vertical scanning (compare column by column), divide and conquer, or binary search on the prefix length." -
"Which approach seems most intuitive?"
β "Vertical scanning is intuitive: check each character position across all strings until we find a mismatch." -
"How is this different from finding the longest common substring?"
β "This only checks prefixes (beginning of strings), not substrings anywhere in the string. This makes it simpler." -
"What determines when we can stop searching?"
β "We stop when we find a character that doesn't match across all strings, or when we reach the end of the shortest string."
-
"Walk me through your approach step by step."
β "Use the first string as a reference. Compare each character position across all strings. When we find a position where characters differ, or reach the end of any string, return the prefix up to that point." -
"What's your strategy for comparing characters?"
β "Iterate through character positions (0, 1, 2, ...). At each position, check if all strings have the same character. If not, we've found where the common prefix ends." -
"How do you handle strings of different lengths?"
β "The common prefix can't be longer than the shortest string, so we either check the shortest string's length upfront, or stop when any string runs out of characters." -
"What's the early termination condition?"
β "Stop as soon as we find a character that doesn't match across all strings, or when we've processed the entire shortest string." -
"Could you use sorting to help?"
β "Yes! After sorting, we only need to compare the first and last strings. If they share a prefix, all middle strings must share it too."
def longestCommonPrefix(strs: list[str]) -> str:
if not strs:
return ""
# Use first string as reference
for i in range(len(strs[0])):
char = strs[0][i]
# Check if this character matches in all other strings
for string in strs[1:]:
# If we've reached the end of this string or characters don't match
if i >= len(string) or string[i] != char:
return strs[0][:i]
# If we've checked all characters in first string, it's the common prefix
return strs[0]def longestCommonPrefix(strs: list[str]) -> str:
if not strs:
return ""
# Start with first string as prefix
prefix = strs[0]
# Compare with each string and reduce prefix
for string in strs[1:]:
# Reduce prefix until it matches the beginning of current string
while not string.startswith(prefix):
prefix = prefix[:-1]
if not prefix:
return ""
return prefixdef longestCommonPrefix(strs: list[str]) -> str:
if not strs:
return ""
# Sort the array
strs.sort()
# Only need to compare first and last strings
first = strs[0]
last = strs[-1]
# Find common prefix between first and last
i = 0
while i < len(first) and i < len(last) and first[i] == last[i]:
i += 1
return first[:i]def longestCommonPrefix(strs: list[str]) -> str:
if not strs:
return ""
# Get lexicographically smallest and largest strings
# They will differ most, so if they share a prefix, all do
min_str = min(strs)
max_str = max(strs)
# Find common prefix
for i in range(len(min_str)):
if min_str[i] != max_str[i]:
return min_str[:i]
return min_strdef longestCommonPrefix(strs: list[str]) -> str:
if not strs:
return ""
# Zip groups characters at same position across all strings
prefix = []
for chars in zip(*strs):
# If all characters at this position are the same
if len(set(chars)) == 1:
prefix.append(chars[0])
else:
break
return ''.join(prefix)-
"What edge cases should we test?"
β "Empty array, array with one string, no common prefix, all strings identical, one string is prefix of others, strings with different lengths, empty string in array." -
"Can you trace through an example?"
β "Let's trace ['flower', 'flow', 'flight']: Position 0: f=f=f β, Position 1: l=l=l β, Position 2: o=o=i β β return 'fl'." -
"Trace through when there's no common prefix?"
β "['dog', 'racecar', 'car']: Position 0: dβ r β return ''." -
"How does the sorting approach work?"
β "After sorting ['flower', 'flow', 'flight'] β ['flight', 'flow', 'flower'], compare first 'flight' and last 'flower'. They differ at position 2, so common prefix is 'fl'." -
"Could you write some test cases?"
def test_longestCommonPrefix():
# Test case 1: Normal case with common prefix
assert longestCommonPrefix(["flower", "flow", "flight"]) == "fl"
# Test case 2: No common prefix
assert longestCommonPrefix(["dog", "racecar", "car"]) == ""
# Test case 3: Single string
assert longestCommonPrefix(["alone"]) == "alone"
# Test case 4: All strings identical
assert longestCommonPrefix(["test", "test", "test"]) == "test"
# Test case 5: Empty array
assert longestCommonPrefix([]) == ""
# Test case 6: One string is prefix of all others
assert longestCommonPrefix(["flower", "flow", "flo"]) == "flo"
# Test case 7: Empty string in array
assert longestCommonPrefix(["abc", "", "ab"]) == ""
# Test case 8: Single characters, no match
assert longestCommonPrefix(["a", "b", "c"]) == ""
# Test case 9: Single characters, match
assert longestCommonPrefix(["a", "a", "a"]) == "a"
# Test case 10: Different lengths, common prefix
assert longestCommonPrefix(["interspecies", "interstellar", "interstate"]) == "inters"-
"What's the time and space complexity of the vertical scanning approach?"
β "Time: O(S) where S is the sum of all characters in all strings. In worst case (all strings identical), we check every character. Space: O(1) for the algorithm itself." -
"What about the horizontal scanning approach?"
β "Time: O(S) as well, but it might be slower in practice because of repeated string operations. Space: O(1) if we don't count the prefix variable." -
"What's the complexity of the sorting approach?"
β "Time: O(S * log n) where n is the number of strings, due to sorting. After sorting, comparison is O(m) where m is the length of the shortest string. Space: O(1) if sorting in-place, or O(S) for the sort." -
"Which approach is best?"
β "Vertical scanning is usually best for this problem: it's intuitive, has optimal O(S) time complexity, and terminates early. Sorting is elegant but has overhead. The zip approach is Pythonic but may be less readable for some." -
"When would sorting be advantageous?"
β "When the strings are already sorted or nearly sorted, or when you need to perform multiple queries on the same dataset." -
"How do these compare in the best case?"
β "Best case: when the first character differs or first string is empty. Vertical scanning: O(n) just checks first char of each string. Horizontal: O(1) finds mismatch immediately. Sorting: O(S * log n) can't avoid sorting." -
"What about the worst case?"
β "Worst case: all strings are identical. All approaches need to check all characters, so O(S)." -
"Any optimization for very long strings?"
β "Binary search on the prefix length: check if a prefix of length mid is common, then search left or right half. Time complexity: O(S * log m) where m is minimum string length."
def longestCommonPrefix(strs: list[str]) -> str:
if not strs:
return ""
def is_common_prefix(length):
"""Check if prefix of given length is common to all strings"""
prefix = strs[0][:length]
return all(s.startswith(prefix) for s in strs)
# Binary search on prefix length
min_len = min(len(s) for s in strs)
left, right = 0, min_len
while left <= right:
mid = (left + right) // 2
if is_common_prefix(mid):
left = mid + 1
else:
right = mid - 1
return strs[0][:right]Time Complexity: O(S * log m) where S is sum of all characters, m is minimum length
Space Complexity: O(1)
| Approach | Time | Space | Pros | Cons |
|---|---|---|---|---|
| Vertical Scanning | O(S) | O(1) | Optimal, early termination | - |
| Horizontal Scanning | O(S) | O(1) | Simple logic | More string operations |
| Sorting | O(S * log n) | O(1)/O(S) | Elegant, only compare 2 strings | Sorting overhead |
| Zip | O(S) | O(m) | Pythonic | Less intuitive, creates tuples |
| Binary Search | O(S * log m) | O(1) | Good for very long strings | More complex |
Recommendation: Use vertical scanning for most cases due to its optimal complexity and clarity.