Longest Common Prefix in DSA Rust - Time & Space Complexity
We want to understand how the time needed to find the longest common prefix changes as we add more words or longer words.
How does the work grow when input size grows?
Analyze the time complexity of the following code snippet.
fn longest_common_prefix(strs: &[String]) -> String {
if strs.is_empty() {
return String::new();
}
let mut prefix = strs[0].clone();
for s in strs.iter().skip(1) {
while !s.starts_with(&prefix) {
prefix.pop();
if prefix.is_empty() {
return String::new();
}
}
}
prefix
}
This code finds the longest common prefix string among an array of strings by shrinking the prefix until all strings start with it.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Checking if each string starts with the current prefix and shortening the prefix if not.
- How many times: For each of the n strings, the prefix may be shortened up to m times, where m is the length of the first string.
As the number of strings (n) or the length of the prefix (m) grows, the number of checks grows roughly by n times m.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 strings, prefix length 5 | About 50 checks |
| 100 strings, prefix length 10 | About 1000 checks |
| 1000 strings, prefix length 20 | About 20,000 checks |
Pattern observation: The work grows roughly by multiplying the number of strings by the prefix length.
Time Complexity: O(n * m)
This means the time grows proportionally to the number of strings times the length of the prefix.
[X] Wrong: "The time depends only on the number of strings, not their length."
[OK] Correct: The prefix length affects how many times we shorten and check strings, so longer prefixes mean more work.
Understanding this helps you explain how string operations affect performance and shows you can analyze nested loops clearly.
"What if we sorted the strings first and only compared the first and last strings? How would the time complexity change?"
Practice
Solution
Step 1: Understand the prefix concept
A prefix is a substring starting at the beginning of a string.Step 2: Define longest common prefix
The longest common prefix is the longest substring starting at the beginning that all strings share.Final Answer:
The longest starting substring common to all strings -> Option CQuick Check:
Prefix means start of strings = A [OK]
- Confusing prefix with substring anywhere
- Choosing shortest string instead of common start
- Thinking prefix is the last part of strings
Solution
Step 1: Identify correct vector initialization syntax
In Rust, vec![...] creates a vector with elements inside square brackets.Step 2: Check syntaxes
vec![...] is correct. [...] without vec! creates an array, not a vector. vec!() uses incorrect parentheses. vec<str>[...] is invalid syntax.Final Answer:
let words = vec!["flower", "flow", "flight"]; -> Option AQuick Check:
vec![...] is correct vector syntax [OK]
- Using parentheses instead of square brackets in vec!
- Confusing arrays with vectors
- Incorrect generic syntax for vec
fn longest_common_prefix(strs: Vec<&str>) -> String {
if strs.is_empty() { return String::new(); }
let mut prefix = strs[0].to_string();
for s in &strs[1..] {
while !s.starts_with(&prefix) {
prefix.pop();
if prefix.is_empty() { return String::new(); }
}
}
prefix
}
fn main() {
let words = vec!["interview", "internet", "internal"];
println!("{}", longest_common_prefix(words));
}Solution
Step 1: Initialize prefix with first string
Prefix starts as "interview".Step 2: Compare prefix with second string "internet"
"internet".starts_with("interview") is false, so pop last char repeatedly: pop 'w' -> "intervie" (false), pop 'e' -> "intervi" (false), pop 'i' -> "interv" (false), pop 'v' -> "inter" (true, since "internet".starts_with("inter")).Step 3: Compare prefix "inter" with third string "internal"
"internal".starts_with("inter") is true, so prefix remains "inter".Final Answer:
"inter" -> Option BQuick Check:
Common start is "inter" = B [OK]
- Not shrinking prefix correctly
- Assuming full first string is prefix
- Confusing substring anywhere with prefix
fn longest_common_prefix(strs: Vec<&str>) -> String {
if strs.is_empty() { return String::new(); }
let mut prefix = strs[0].to_string();
for s in &strs[1..] {
while !s.starts_with(prefix) {
prefix.pop();
if prefix.is_empty() { return String::new(); }
}
}
prefix
}Solution
Step 1: Identify type mismatch in starts_with
starts_with expects a &str reference, but prefix is String. Passing prefix directly causes type error.Step 2: Fix by passing reference to prefix
Use &prefix to pass a &str reference, matching function signature.Final Answer:
Change s.starts_with(prefix) to s.starts_with(&prefix) -> Option AQuick Check:
Use & prefix for &str parameter [OK]
- Passing String instead of &str to starts_with
- Removing first char instead of last char
- Not checking empty prefix before popping
Solution
Step 1: Understand case-insensitive comparison
To ignore case, convert all strings to the same case (lowercase or uppercase) before comparison.Step 2: Apply lowercase conversion before prefix logic
Transform each string to lowercase, then find longest common prefix normally.Final Answer:
Convert all strings to lowercase before finding prefix -> Option DQuick Check:
Normalize case first for case-insensitive prefix [OK]
- Comparing characters without case normalization
- Using ASCII values without case mapping
- Reversing strings which doesn't solve case issue
Ready to prove it?
Pass the test and this pattern moves unknown
