String · 27 of 43
Longest Common Prefix
Find the longest start that every word in a list shares, by reading all the words together one letter at a time.
- Simple way
- time O(n * m^2)
room O(m) - Clever way
- time O(n * m)
room O(1)
The problem, simply
You get a list of n words. Find the longest piece that all of them start with. That piece is called the common prefix. If they do not even share a first letter, give back an empty string "".
Below, m is the length of the shortest word. The answer can never be longer than that.
A story to remember it
Picture it
Write the words under each other. Read down each column. Columns 0 and 1 agree, column 2 does not.
- answer
- rejected
Examples
A normal case
- In
['flower', 'flow', 'flight']- Out
'fl'
The third letters are o, o and i. They do not agree, so the answer stops after fl.
Nothing shared
- In
['dog', 'racecar', 'car']- Out
''
The first letters d, r and c already disagree.
A word runs out
- In
['ab', 'a']- Out
'a'
The word a has no second letter, so the chant stops after one letter.
One word
- In
['interview']- Out
'interview'
With only one word, the whole word is shared by everyone.
An empty word
- In
['', 'abc']- Out
''
An empty word shares nothing, so the answer is empty.
Way 1: the simple way
Guess the answer's length. Start with the longest possible guess, the length of the shortest word. Check if every word starts with that many letters of the first word. If not, try one letter shorter.
- Find
m, the length of the shortest word. - For
len = mdown to 0, takeguess = first word, cut to len letters. - If every word starts with
guess, return it.
class Solution {
public String longestCommonPrefix(String[] words) {
if (words.length == 0) return "";
int m = Integer.MAX_VALUE;
for (String w : words) m = Math.min(m, w.length());
for (int len = m; len >= 0; len--) {
String guess = words[0].substring(0, len);
boolean everyone = true;
for (String w : words) {
if (!w.startsWith(guess)) { everyone = false; break; }
}
if (everyone) return guess; // longest guess that works
}
return "";
}
}How long it takes: up to m + 1 guesses, and each one checks n words of up to m letters, so O(n * m^2). Extra room: the guess string, O(m).
Way 2: the clever way
Do the class chant. Read column 0 of every word, then column 1, and so on. Stop at the first column where a word disagrees or runs out. Nothing is checked twice.
- For each column
cof the first word, take the letterch = words[0][c]. - For every other word: if it is too short or its letter at
cis notch, stop and return the firstcletters. - If the loop finishes, the whole first word is the answer.
The chant on flower, flow, flight
01 / 05
Line the three words up under each other. Column c starts at 0. The answer so far is empty.
- answer
- ''
- checking
- done
- answer
- rejected
- ignored
class Solution {
public String longestCommonPrefix(String[] words) {
if (words.length == 0) return "";
String first = words[0];
for (int c = 0; c < first.length(); c++) {
char ch = first.charAt(c);
for (int k = 1; k < words.length; k++) {
// this word ran out, or says a different letter
if (c >= words[k].length() || words[k].charAt(c) != ch) {
return first.substring(0, c);
}
}
}
return first; // everyone matched the whole first word
}
}How long it takes: we read at most m + 1 columns of n words, so O(n * m). That is never more than the total number of letters. Extra room: a few counters, O(1), not counting the answer.
Way 3: sort, then compare two words
Sort the words in dictionary order. The first and the last word are the two most different ones. Whatever they share, every word in between shares too. So compare only those two.
import java.util.*;
class Solution {
public String longestCommonPrefix(String[] words) {
if (words.length == 0) return "";
String[] w = words.clone();
Arrays.sort(w);
String a = w[0], b = w[w.length - 1];
int i = 0;
while (i < a.length() && i < b.length() && a.charAt(i) == b.charAt(i)) i++;
return a.substring(0, i);
}
}This is short to write, but sorting costs O(n log n) comparisons of up to m letters each, so O(n * m log n). Good to mention, but Way 2 is faster.
Watch out for
- An empty list has no prefix. Return
""first. - One word is its own answer. Way 2 handles it because the inner loop does nothing.
indexOf(prefix) == 0works but searches the whole word.startsWithis the right check.- Do not sort the caller's array in place unless you are allowed to. Way 3 clones it.
What interviewers ask next
- Many questions about the same word list? Put all words in a letter tree (a trie). Walk down from the top while there is only one branch and no word ends. That path is the prefix.
- Can you split the work? Yes. The prefix of the left half and the prefix of the right half, then the prefix of those two. That is divide and conquer, still
O(n * m). - Binary search on the length? Check if the first
midletters are shared. That givesO(n * m log m), worse here, but it shows you can binary search on an answer. - Longest common suffix? Do the same from the right end of every word.
Remember this
Grown-up words: the chant is vertical scanning, the shared start is the longest common prefix, the letter tree is a trie, and splitting the list in halves is divide and conquer.