Facebook Pixel

14. Longest Common Prefix

Problem Description

Given an array of strings, find the longest common prefix that appears at the beginning of all strings in the array.

A common prefix is a sequence of characters that appears at the start of every string. For example, if you have the strings ["flower", "flow", "flight"], the longest common prefix would be "fl" since all three strings start with these two characters.

The function should:

  • Take an array of strings as input
  • Return the longest string that is a prefix of all strings in the array
  • Return an empty string "" if no common prefix exists

Examples to clarify the problem:

  • Input: ["flower", "flow", "flight"] β†’ Output: "fl"
  • Input: ["dog", "racecar", "car"] β†’ Output: "" (no common prefix)
  • Input: ["interspecies", "interstellar", "interstate"] β†’ Output: "inters"

The solution uses the first string as a reference point and checks character by character if all other strings have the same character at each position. The process stops when either a mismatch is found or when we reach the end of any string. The characters checked up to that point form the longest common prefix.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

When looking for a common prefix among multiple strings, we need to find characters that appear in the same position across all strings. The key insight is that the longest common prefix cannot be longer than the shortest string in the array.

Think of it like stacking the strings vertically and reading column by column:

flower
flow
flight

Reading vertically, the first column has all 'f', the second column has all 'l', but the third column has different characters ('o', 'o', 'i'). This is where the common prefix ends.

We can pick any string as a reference - let's use the first one. Why? Because if there's a common prefix, it must exist in the first string too. We then iterate through each character position i of this first string and check if all other strings have:

  1. At least i+1 characters (otherwise they're too short)
  2. The same character at position i

The moment we find a mismatch or reach the end of any string, we stop. The characters we've successfully matched so far form our longest common prefix.

This vertical scanning approach is intuitive because we're essentially asking: "How far can we go before the strings start to differ?" We process character by character from left to right, building up the common prefix incrementally until we hit a difference or run out of characters to compare.

Learn more about Trie patterns.

Solution Approach

The implementation follows a character comparison strategy using the first string as a benchmark. Here's how the algorithm works:

  1. Use the first string as reference: We iterate through each character index i of strs[0], from position 0 to its length.

  2. Compare with remaining strings: For each character position i, we check all strings from strs[1:] (all strings except the first one).

  3. Two stopping conditions: For each string s in the remaining strings, we check:

    • len(s) <= i: This means the current string s is shorter than or equal to the current index. We've reached the end of this string, so the common prefix can't be longer.
    • s[i] != strs[0][i]: The character at position i in string s doesn't match the character at position i in our reference string. The common prefix ends here.
  4. Return the prefix: When either condition is met, we return s[:i], which gives us the substring from the beginning up to (but not including) index i. Note that we could also return strs[0][:i] - both would give the same result since all strings share this prefix.

  5. Complete match: If we finish iterating through all characters of strs[0] without hitting any stopping condition, it means strs[0] itself is the common prefix for all strings, so we return strs[0].

The algorithm processes characters position by position (vertical scanning), checking all strings at each position before moving to the next. This ensures we find the exact point where the strings begin to differ, giving us the longest common prefix.

Time complexity: O(S) where S is the sum of all characters in all strings. In the worst case, all strings are identical. Space complexity: O(1) as we only use a constant amount of extra space.

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the algorithm with the input ["flower", "flow", "flight"].

Initial Setup:

  • Use strs[0] = "flower" as our reference string
  • We'll check each character position i from 0 to 5 (length of "flower" is 6)

Iteration 1 (i = 0):

  • Check character at position 0 in "flower": 'f'
  • Compare with "flow"[0]: 'f' βœ“ (matches)
  • Compare with "flight"[0]: 'f' βœ“ (matches)
  • All match, continue to next position

Iteration 2 (i = 1):

  • Check character at position 1 in "flower": 'l'
  • Compare with "flow"[1]: 'l' βœ“ (matches)
  • Compare with "flight"[1]: 'l' βœ“ (matches)
  • All match, continue to next position

Iteration 3 (i = 2):

  • Check character at position 2 in "flower": 'o'
  • Compare with "flow"[2]: 'o' βœ“ (matches)
  • Compare with "flight"[2]: 'i' βœ— (mismatch!)
  • Found a mismatch at position 2
  • Return strs[0][:2] which is "fl"

The algorithm correctly identifies that the common prefix ends at position 2, returning "fl" as the longest common prefix.

Alternative scenario: If we had ["flow", "flower", "flowing"] instead:

  • At i = 4, we'd check position 4 in "flow" (but "flow" only has 4 characters, indices 0-3)
  • Since we've reached the end of our reference string without any mismatches, we return the entire reference string: "flow"

Solution Implementation

1class Solution:
2    def longestCommonPrefix(self, strs: List[str]) -> str:
3        # Iterate through each character position of the first string
4        for i in range(len(strs[0])):
5            # Check this position against all other strings in the array
6            for string in strs[1:]:
7                # If current string is shorter than position i, or
8                # character at position i doesn't match the first string's character
9                if len(string) <= i or string[i] != strs[0][i]:
10                    # Return the common prefix up to (but not including) position i
11                    return strs[0][:i]
12      
13        # If we've checked all positions without mismatch,
14        # the entire first string is the common prefix
15        return strs[0]
16
1class Solution {
2    /**
3     * Finds the longest common prefix string amongst an array of strings.
4     * 
5     * @param strs Array of strings to find common prefix from
6     * @return The longest common prefix, or empty string if no common prefix exists
7     */
8    public String longestCommonPrefix(String[] strs) {
9        // Get the total number of strings in the array
10        int numberOfStrings = strs.length;
11      
12        // Iterate through each character position of the first string
13        for (int charIndex = 0; charIndex < strs[0].length(); charIndex++) {
14            // Compare this character position across all other strings
15            for (int stringIndex = 1; stringIndex < numberOfStrings; stringIndex++) {
16                // Check if current string is shorter than current position
17                // or if character at current position doesn't match first string
18                if (strs[stringIndex].length() <= charIndex || 
19                    strs[stringIndex].charAt(charIndex) != strs[0].charAt(charIndex)) {
20                    // Return the common prefix found so far
21                    return strs[0].substring(0, charIndex);
22                }
23            }
24        }
25      
26        // If we've checked all characters of first string without mismatch,
27        // the entire first string is the common prefix
28        return strs[0];
29    }
30}
31
1class Solution {
2public:
3    string longestCommonPrefix(vector<string>& strs) {
4        // Get the number of strings in the array
5        int numStrings = strs.size();
6      
7        // Iterate through each character position of the first string
8        for (int charIndex = 0; charIndex < strs[0].size(); ++charIndex) {
9            // Compare this character position across all other strings
10            for (int stringIndex = 1; stringIndex < numStrings; ++stringIndex) {
11                // Check if current string is shorter than current position
12                // or if the character at this position doesn't match the first string
13                if (strs[stringIndex].size() <= charIndex || 
14                    strs[stringIndex][charIndex] != strs[0][charIndex]) {
15                    // Return the common prefix found so far
16                    return strs[0].substr(0, charIndex);
17                }
18            }
19        }
20      
21        // If we've checked all characters of the first string without mismatch,
22        // the entire first string is the common prefix
23        return strs[0];
24    }
25};
26
1/**
2 * Finds the longest common prefix string amongst an array of strings.
3 * @param strs - Array of strings to find common prefix from
4 * @returns The longest common prefix string, or empty string if no common prefix exists
5 */
6function longestCommonPrefix(strs: string[]): string {
7    // Find the minimum length among all strings to limit our search range
8    const minLength: number = strs.reduce(
9        (currentMin: number, str: string) => Math.min(currentMin, str.length), 
10        Infinity
11    );
12  
13    // Try prefixes from longest to shortest possible length
14    for (let prefixLength: number = minLength; prefixLength > 0; prefixLength--) {
15        // Extract the candidate prefix from the first string
16        const candidatePrefix: string = strs[0].slice(0, prefixLength);
17      
18        // Check if all strings start with this candidate prefix
19        const isCommonPrefix: boolean = strs.every(
20            (str: string) => str.slice(0, prefixLength) === candidatePrefix
21        );
22      
23        if (isCommonPrefix) {
24            return candidatePrefix;
25        }
26    }
27  
28    // No common prefix found
29    return '';
30}
31

Time and Space Complexity

The time complexity is O(n Γ— m), where n is the number of strings in the array and m is the length of the shortest string (or the length of the common prefix, whichever is smaller). In the worst case, we need to compare each character of the first string with the corresponding character in all other strings. The outer loop runs at most m times (length of the first string or until a mismatch is found), and the inner loop runs n-1 times for each iteration of the outer loop.

The space complexity is O(1). The algorithm only uses a constant amount of extra space for the loop variables i and s. The returned string is a substring of an existing string (using slicing), which in Python creates a new string, but this is considered part of the output and not counted as auxiliary space used by the algorithm itself.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Empty Array Input

The Problem: The code assumes strs[0] exists and will throw an IndexError if the input array is empty.

Example:

  • Input: [] β†’ Raises IndexError: list index out of range

Solution: Add a guard clause to handle empty arrays:

def longestCommonPrefix(self, strs: List[str]) -> str:
    if not strs:  # Handle empty array
        return ""
  
    for i in range(len(strs[0])):
        for string in strs[1:]:
            if len(string) <= i or string[i] != strs[0][i]:
                return strs[0][:i]
    return strs[0]

2. Single String in Array

The Problem: While the current code handles this correctly, it's inefficient as it still iterates through an empty list strs[1:]. This is a minor performance consideration but worth noting.

Example:

  • Input: ["hello"] β†’ The loop for string in strs[1:] iterates over an empty list

Solution: Add an early return for single-element arrays:

def longestCommonPrefix(self, strs: List[str]) -> str:
    if not strs:
        return ""
    if len(strs) == 1:  # Single string is its own prefix
        return strs[0]
  
    for i in range(len(strs[0])):
        for string in strs[1:]:
            if len(string) <= i or string[i] != strs[0][i]:
                return strs[0][:i]
    return strs[0]

3. Confusion About Return Value in Mismatch

The Problem: When returning s[:i] instead of strs[0][:i], developers might worry about edge cases where s is shorter than i. However, this concern is unfounded because the condition len(s) <= i ensures we return before accessing s[i].

Clarification: Both return s[:i] and return strs[0][:i] are correct because:

  • If len(s) <= i, then s[:i] returns the entire string s, which is indeed the common prefix
  • If s[i] != strs[0][i], then both strings share the same prefix up to index i-1

Recommended approach for clarity:

# Always return from the reference string for consistency
return strs[0][:i]

4. Alternative Edge Case - Array Contains Empty String

The Problem: If any string in the array is empty, the longest common prefix must be empty. The current solution handles this correctly but it's worth understanding why.

Example:

  • Input: ["flower", "", "flight"] β†’ Output: ""

The code handles this naturally because when checking the empty string:

  • len("") <= 0 is True for the first character check
  • Returns strs[0][:0] which is ""

No code change needed, but understanding this behavior prevents confusion during debugging.

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

Which data structure is used to implement recursion?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More