2937. Make Three Strings Equal
Problem Description
You have three strings: s1
, s2
, and s3
. You can perform operations on these strings where each operation consists of deleting the rightmost character from any one of the three strings. However, you cannot delete all characters from a string - each string must always have at least one character remaining.
Your goal is to make all three strings equal through these deletion operations. You need to find the minimum number of operations required to achieve this.
For example, if you have strings "abc", "ab", and "abcd", you can:
- Delete 'c' from "abc" to get "ab"
- Delete 'd' from "abcd" to get "abc"
- Delete 'c' from "abc" to get "ab" Now all three strings are "ab" after 3 operations.
The problem asks you to return the minimum number of deletion operations needed. If it's impossible to make all three strings equal (which happens when they don't share a common prefix), return -1
.
The key insight is that since you can only delete from the right side of strings, the final equal strings must be a common prefix of all three original strings. The longest such common prefix that's at least 1 character long will give you the minimum number of deletions needed.
Intuition
Since we can only delete characters from the right side of each string, think about what the final equal strings would look like. They must be prefixes of all three original strings. Why? Because we're removing characters from the end, so whatever remains must have been at the beginning of each string.
This means for the three strings to become equal, they must share a common starting portion. For instance, if we have "hello", "help", and "helmet", they all start with "hel", so we could potentially make them all equal to "hel", "he", or just "h".
The key realization is that we want to keep as many characters as possible to minimize deletions. So we should find the longest common prefix among all three strings. Once we know this prefix length, the total number of deletions needed is straightforward to calculate.
Let's trace through the logic:
- We need to check character by character from the start of all three strings
- As long as
s1[i] == s2[i] == s3[i]
, we have a valid common prefix up to positioni
- The moment we find a position where the characters differ, or we reach the end of the shortest string, we stop
- If there's no common prefix at all (characters differ at position 0), it's impossible to make them equal, so we return
-1
- Otherwise, the number of operations equals the total characters we need to remove:
(len(s1) + len(s2) + len(s3)) - 3 * common_prefix_length
The formula makes sense because we're removing all characters except the common prefix from each string. If the common prefix has length k
, each string keeps k
characters, so we keep 3k
characters total and delete the rest.
Solution Approach
The implementation follows a straightforward enumeration approach to find the longest common prefix:
-
Calculate total characters: First, we compute
s = len(s1) + len(s2) + len(s3)
, which represents the total number of characters across all three strings. This will be useful for calculating the final answer. -
Find the enumeration boundary: We determine
n = min(len(s1), len(s2), len(s3))
. This is the maximum possible length of any common prefix, since we can't have a common prefix longer than the shortest string. -
Enumerate prefix positions: We iterate through positions
i
from0
ton-1
, checking if the characters at positioni
are the same across all three strings using the conditions1[i] == s2[i] == s3[i]
. -
Handle mismatch cases: When we find the first position where characters don't match:
- If
i == 0
, it means there's no common prefix at all (the very first characters are different), so we return-1
- Otherwise, the common prefix has length
i
, and we returns - 3 * i
(total characters minus the characters we keep)
- If
-
Handle complete prefix case: If the loop completes without finding a mismatch, it means the entire shortest string is a common prefix. In this case, the common prefix length is
n
, and we returns - 3 * n
.
The algorithm runs in O(n)
time where n
is the length of the shortest string, and uses O(1)
extra space. The elegance of this solution lies in its direct approach - we simply check how far the common prefix extends and calculate the deletions needed based on that length.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with strings s1 = "abc"
, s2 = "abb"
, and s3 = "ab"
.
Step 1: Calculate total characters
- Total characters:
s = 3 + 3 + 2 = 8
Step 2: Find enumeration boundary
- Minimum length:
n = min(3, 3, 2) = 2
- We can only check positions 0 and 1 (indices up to n-1)
Step 3: Check character by character
Position i = 0:
s1[0] = 'a'
,s2[0] = 'a'
,s3[0] = 'a'
- All characters match! Continue to next position.
Position i = 1:
s1[1] = 'b'
,s2[1] = 'b'
,s3[1] = 'b'
- All characters match! Continue to next position.
Step 4: Loop completes
- We've checked all positions up to the shortest string's length
- The common prefix length is
n = 2
(the string "ab")
Step 5: Calculate operations needed
- Operations =
s - 3 * n = 8 - 3 * 2 = 8 - 6 = 2
Verification:
- Delete 'c' from "abc" → "ab" (1 operation)
- Delete 'b' from "abb" → "ab" (1 operation)
- "ab" stays as "ab" (0 operations)
- Total: 2 operations ✓
The strings all become "ab" after 2 deletion operations, which matches our calculation.
Solution Implementation
1class Solution:
2 def findMinimumOperations(self, s1: str, s2: str, s3: str) -> int:
3 # Calculate total length of all three strings
4 total_length = len(s1) + len(s2) + len(s3)
5
6 # Find the minimum length among the three strings
7 min_length = min(len(s1), len(s2), len(s3))
8
9 # Iterate through characters up to the minimum length
10 for i in range(min_length):
11 # Check if characters at position i are the same in all three strings
12 if not (s1[i] == s2[i] == s3[i]):
13 # If first characters don't match, impossible to make strings equal
14 if i == 0:
15 return -1
16 # Return operations needed: total length minus 3 times the common prefix length
17 return total_length - 3 * i
18
19 # All characters match up to min_length
20 # Return operations needed to remove extra characters
21 return total_length - 3 * min_length
22
1class Solution {
2 public int findMinimumOperations(String s1, String s2, String s3) {
3 // Calculate total length of all three strings
4 int totalLength = s1.length() + s2.length() + s3.length();
5
6 // Find the minimum length among the three strings
7 int minLength = Math.min(Math.min(s1.length(), s2.length()), s3.length());
8
9 // Iterate through characters up to the minimum length
10 for (int i = 0; i < minLength; i++) {
11 // Check if characters at position i are equal in all three strings
12 if (!(s1.charAt(i) == s2.charAt(i) && s2.charAt(i) == s3.charAt(i))) {
13 // If first character doesn't match, strings cannot be made equal
14 if (i == 0) {
15 return -1;
16 }
17 // Return total operations needed: remove characters after common prefix
18 // Each string keeps i characters, so we remove (totalLength - 3*i) characters
19 return totalLength - 3 * i;
20 }
21 }
22
23 // All characters match up to minimum length
24 // Keep minLength characters in each string, remove the rest
25 return totalLength - 3 * minLength;
26 }
27}
28
1class Solution {
2public:
3 int findMinimumOperations(string s1, string s2, string s3) {
4 // Calculate total length of all three strings
5 int totalLength = s1.size() + s2.size() + s3.size();
6
7 // Find the minimum length among the three strings
8 int minLength = min({s1.size(), s2.size(), s3.size()});
9
10 // Check how many characters match from the beginning
11 for (int i = 0; i < minLength; ++i) {
12 // If characters at position i don't match in all three strings
13 if (!(s1[i] == s2[i] && s2[i] == s3[i])) {
14 // If no characters match at all (i == 0), return -1 (impossible)
15 // Otherwise, return total operations needed:
16 // total length - (3 * number of matching characters)
17 return (i == 0) ? -1 : totalLength - 3 * i;
18 }
19 }
20
21 // All characters up to minLength match
22 // Return operations needed to remove extra characters
23 return totalLength - 3 * minLength;
24 }
25};
26
1/**
2 * Finds the minimum number of operations needed to make three strings equal
3 * by removing characters from the end of each string.
4 *
5 * @param s1 - The first string
6 * @param s2 - The second string
7 * @param s3 - The third string
8 * @returns The minimum number of operations, or -1 if impossible
9 */
10function findMinimumOperations(s1: string, s2: string, s3: string): number {
11 // Calculate the total length of all three strings
12 const totalLength: number = s1.length + s2.length + s3.length;
13
14 // Find the minimum length among the three strings
15 const minLength: number = Math.min(s1.length, s2.length, s3.length);
16
17 // Iterate through characters up to the minimum length
18 for (let i: number = 0; i < minLength; ++i) {
19 // Check if characters at position i are different across the strings
20 if (!(s1[i] === s2[i] && s2[i] === s3[i])) {
21 // If first characters don't match, it's impossible to make strings equal
22 if (i === 0) {
23 return -1;
24 }
25 // Return total operations needed: total length minus 3 times the common prefix length
26 return totalLength - 3 * i;
27 }
28 }
29
30 // All characters match up to minimum length
31 // Return operations needed to remove excess characters
32 return totalLength - 3 * minLength;
33}
34
Time and Space Complexity
The time complexity is O(n)
, where n
is the minimum length of the three strings. The algorithm iterates through the characters of all three strings simultaneously, but only up to the length of the shortest string. In the worst case, it examines each character position once until it finds a mismatch or reaches the end of the shortest string.
The space complexity is O(1)
. The algorithm only uses a constant amount of extra space to store variables s
(the sum of lengths), n
(the minimum length), and i
(the loop counter). No additional data structures that grow with input size are created.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Forgetting the "at least one character" constraint
A common mistake is not properly handling the edge case where strings don't share any common prefix. Developers might forget to check if i == 0
before returning the result, leading to returning total_length - 0
(which would mean deleting all characters) instead of -1
.
Incorrect implementation:
for i in range(min_length):
if not (s1[i] == s2[i] == s3[i]):
return total_length - 3 * i # Wrong! Doesn't check if i == 0
Correct implementation:
for i in range(min_length):
if not (s1[i] == s2[i] == s3[i]):
if i == 0: # Must check this first
return -1
return total_length - 3 * i
2. Off-by-one error in calculation
Another pitfall is miscalculating the number of operations. Some might incorrectly think they need to keep i+1
characters when the mismatch occurs at position i
, or confuse the index with the count.
Incorrect calculation:
return total_length - 3 * (i + 1) # Wrong! When mismatch at i, we keep i characters, not i+1
Correct calculation:
return total_length - 3 * i # Keep exactly i characters (indices 0 to i-1)
3. Not handling the complete match case
If all characters match up to min_length
, some implementations might forget to return a value, causing the function to return None
or throw an error.
Incomplete implementation:
def findMinimumOperations(self, s1: str, s2: str, s3: str) -> int:
total_length = len(s1) + len(s2) + len(s3)
min_length = min(len(s1), len(s2), len(s3))
for i in range(min_length):
if not (s1[i] == s2[i] == s3[i]):
if i == 0:
return -1
return total_length - 3 * i
# Missing the return statement for complete match!
Complete implementation:
def findMinimumOperations(self, s1: str, s2: str, s3: str) -> int:
total_length = len(s1) + len(s2) + len(s3)
min_length = min(len(s1), len(s2), len(s3))
for i in range(min_length):
if not (s1[i] == s2[i] == s3[i]):
if i == 0:
return -1
return total_length - 3 * i
return total_length - 3 * min_length # Must handle this case
4. Using incorrect comparison syntax
Some developers might use the wrong syntax for comparing three values simultaneously, leading to unexpected behavior due to Python's chaining comparison operators.
Potentially confusing:
if s1[i] == s2[i] and s2[i] == s3[i]: # Works but verbose
Clear and concise:
if s1[i] == s2[i] == s3[i]: # Python's chained comparison - clean and correct
Note that using not (s1[i] == s2[i] == s3[i])
is clearer than s1[i] != s2[i] or s2[i] != s3[i]
for checking if any characters differ.
What data structure does Breadth-first search typically uses to store intermediate states?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!