Skip to content

Instantly share code, notes, and snippets.

@ascherj
Created October 30, 2025 04:21
Show Gist options
  • Select an option

  • Save ascherj/fdd0c58d33d499fa5319e6c5f2a273c7 to your computer and use it in GitHub Desktop.

Select an option

Save ascherj/fdd0c58d33d499fa5319e6c5f2a273c7 to your computer and use it in GitHub Desktop.
πŸ§‘β€πŸ’» Interviewer Guide: Longest Common Prefix (UMPIRE Method)

πŸ§‘β€πŸ’» Interviewer Guide: Longest Common Prefix (UMPIRE Method)

https://leetcode.com/problems/longest-common-prefix/

πŸ“Œ Problem Statement

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 "".


🧭 U β€” Understand the Problem

Interviewer Prompts & Possible Answers:

  • "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'."


πŸ§ͺ M β€” Match with Patterns

Interviewer Prompts & Possible Answers:

  • "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."


πŸ› οΈ P β€” Plan the Solution

Interviewer Prompts & Possible Answers:

  • "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."


⌨️ I β€” Implement

Main Solution (Vertical Scanning)

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]

Alternative Implementation (Horizontal Scanning)

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 prefix

Optimized Implementation (Sorting)

def 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]

Implementation Using Min/Max

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_str

Implementation Using Zip (Pythonic)

def 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)

πŸ” R β€” Review

Interviewer Prompts & Possible Answers:

  • "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"

🧹 E β€” Evaluate & Optimize

Interviewer Prompts & Possible Answers:

  • "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."

Advanced: Binary Search Implementation (Optional)

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)

Comparison Table

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment