3135. Equalize Strings by Adding or Removing Characters at Ends 🔒
Problem Description
Given two strings initial
and target
, your task is to modify initial
by performing a series of operations to make it equal to target
.
In one operation, you can add or remove one character only at the beginning or the end of the string initial
.
Return the minimum number of operations required to transform initial
into target
.
Intuition
To solve this problem, we need to minimize the number of operations required to transform the string initial
into the string target
. The key insight is to find the longest common substring that can exist between initial
and target
, because by identifying the longest identical sequence present in both strings, we minimize changes needed on initial
.
-
Identify the Longest Common Substring: By using dynamic programming, we can determine the length of the longest common substring between
initial
andtarget
. This substring represents the part ofinitial
that is also present intarget
, requiring no changes. -
Calculate Minimum Operations: Once we have the length of this longest common substring, denoted as
mx
, the number of operations required to makeinitial
intotarget
can be calculated as:- We have to remove characters not part of the substring from
initial
, which costsm - mx
operations (wherem
is the length ofinitial
). - Similarly, we have to add characters to reconstruct
target
, which costsn - mx
operations (wheren
is the length oftarget
). - Therefore, the total operations required are:
m + n - 2 * mx
.
- We have to remove characters not part of the substring from
By leveraging dynamic programming to find the longest common substring, this approach efficiently leads us to the minimum number of operations required.
Learn more about Binary Search, Dynamic Programming and Sliding Window patterns.
Solution Approach
To solve the problem, a dynamic programming approach is employed to find the longest common substring between the two strings, initial
and target
. Here’s a detailed breakdown of the solution approach:
-
Define the Dynamic Programming Table:
- We use a two-dimensional array
f
, wheref[i][j]
represents the length of the longest common substring ending atinitial[i - 1]
andtarget[j - 1]
.
- We use a two-dimensional array
-
Initialize the Table:
- The
f
table is initialized to zero. This size is(m+1) x (n+1)
, wherem
is the length ofinitial
andn
is the length oftarget
.
- The
-
State Transition:
- If the characters
initial[i-1]
andtarget[j-1]
are the same, thenf[i][j] = f[i-1][j-1] + 1
. This captures the continuation of a common substring. - Otherwise,
f[i][j] = 0
.
- If the characters
-
Track Maximum Length:
- We maintain a variable
mx
to keep track of the maximum value found in thef
table, which represents the length of the longest common substring.
- We maintain a variable
-
Calculate Minimum Operations:
- The minimum number of operations required to transform
initial
intotarget
is given by the formula:m + n - 2 * mx
.
- The minimum number of operations required to transform
Here is the implementation in code:
class Solution:
def minOperations(self, initial: str, target: str) -> int:
m, n = len(initial), len(target)
f = [[0] * (n + 1) for _ in range(m + 1)]
mx = 0
for i, a in enumerate(initial, 1):
for j, b in enumerate(target, 1):
if a == b:
f[i][j] = f[i - 1][j - 1] + 1
mx = max(mx, f[i][j])
return m + n - mx * 2
This code effectively finds the longest common substring length and computes the transformations needed by leveraging dynamic programming.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through an example to illustrate how the solution approach works.
Example:
initial = "abcde"
target = "acd"
Step 1: Define the Dynamic Programming Table
We create a dynamic programming table f
with dimensions (m+1) x (n+1)
, where m = 5
(length of "abcde") and n = 3
(length of "acd"). This table is initially filled with zeros:
"" a c d "" 0 0 0 0 a 0 0 0 0 b 0 0 0 0 c 0 0 0 0 d 0 0 0 0 e 0 0 0 0
Step 2: State Transition
We iterate over each character pair (initial[i-1], target[j-1])
and update the table:
-
When
i=1
,a="a"
, matchingj=1
,b="a"
:f[1][1] = f[0][0] + 1 = 1
- Update the table:
"" a c d "" 0 0 0 0 a 0 1 0 0 b 0 0 0 0 c 0 0 0 0 d 0 0 0 0 e 0 0 0 0
-
When
i=3
,a="c"
, matchingj=2
,b="c"
:f[3][2] = f[2][1] + 1 = 1
- Update the table:
"" a c d "" 0 0 0 0 a 0 1 0 0 b 0 0 0 0 c 0 0 1 0 d 0 0 0 0 e 0 0 0 0
-
When
i=4
,a="d"
, matchingj=3
,b="d"
:f[4][3] = f[3][2] + 1 = 2
- Update the table:
"" a c d "" 0 0 0 0 a 0 1 0 0 b 0 0 0 0 c 0 0 1 0 d 0 0 0 2 e 0 0 0 0
Step 3: Track Maximum Length
We find the maximum value in the table, mx = 2
, which is the longest common substring length.
Step 4: Calculate Minimum Operations
Calculate the required operations:
- Length of
initial
(m) = 5 - Length of
target
(n) = 3 mx = 2
The formula is m + n - 2 * mx
:
5 + 3 - 2 * 2 = 4
So, the minimum number of operations needed is 4.
This walkthrough demonstrates finding the longest common substring using dynamic programming and calculating the minimum operations needed to transform initial
into target
.
Solution Implementation
1class Solution:
2 def minOperations(self, initial: str, target: str) -> int:
3 # Lengths of the initial and target strings
4 m, n = len(initial), len(target)
5
6 # Initialize a 2D list (matrix) to compute the longest common subsequence
7 # The size is (m+1) x (n+1) with default values of 0
8 f = [[0] * (n + 1) for _ in range(m + 1)]
9
10 # Variable to track the maximum length of common subsequence seen so far
11 max_length = 0
12
13 # Loop through each character of the initial string with index starting from 1
14 for i, char_initial in enumerate(initial, 1):
15 # Loop through each character of the target string with index starting from 1
16 for j, char_target in enumerate(target, 1):
17 # If the characters match, update the matrix at f[i][j]
18 # by adding 1 to the value at the diagonal position f[i-1][j-1]
19 if char_initial == char_target:
20 f[i][j] = f[i - 1][j - 1] + 1
21 # Update the maximum length if current subsequence is greater
22 max_length = max(max_length, f[i][j])
23
24 # Return the minimum number of operations required to make the strings equal
25 # This is calculated by subtracting twice the maximum common subsequence length
26 # from the sum of the lengths of the two strings
27 return m + n - max_length * 2
28
1class Solution {
2 public int minOperations(String initial, String target) {
3 // Get lengths of the initial and target strings
4 int lengthInitial = initial.length();
5 int lengthTarget = target.length();
6
7 // Create a 2D array for dynamic programming, initialized to 0
8 int[][] commonSubstringLength = new int[lengthInitial + 1][lengthTarget + 1];
9
10 // Variable to store the maximum length of common subsequence found
11 int maxCommonLength = 0;
12
13 // Fill the array using dynamic programming approach
14 for (int i = 1; i <= lengthInitial; ++i) {
15 for (int j = 1; j <= lengthTarget; ++j) {
16 // Check if characters current in both strings match
17 if (initial.charAt(i - 1) == target.charAt(j - 1)) {
18 // If match, extend the length of common substring by 1
19 commonSubstringLength[i][j] = commonSubstringLength[i - 1][j - 1] + 1;
20 // Update the maximum common length found so far
21 maxCommonLength = Math.max(maxCommonLength, commonSubstringLength[i][j]);
22 }
23 }
24 }
25
26 // Calculate minimum operations needed
27 return lengthInitial + lengthTarget - 2 * maxCommonLength;
28 }
29}
30
1#include <string>
2#include <vector>
3#include <cstring>
4#include <algorithm>
5
6class Solution {
7public:
8 int minOperations(std::string initial, std::string target) {
9 int m = initial.size(); // Length of the initial string
10 int n = target.size(); // Length of the target string
11
12 // Dynamic programming table to store lengths of longest common subsequences
13 int dp[m + 1][n + 1];
14 std::memset(dp, 0, sizeof(dp)); // Initialize the dp array with zero
15
16 int maxLength = 0; // Store the length of the longest common subsequence
17
18 // Populate the dp table
19 for (int i = 1; i <= m; ++i) {
20 for (int j = 1; j <= n; ++j) {
21 // If characters match, increment the length of LCS by 1
22 if (initial[i - 1] == target[j - 1]) {
23 dp[i][j] = dp[i - 1][j - 1] + 1;
24 maxLength = std::max(maxLength, dp[i][j]); // Update the maxLength
25 }
26 }
27 }
28
29 // Minimum operations needed is the total length of both strings minus two times the length of longest common subsequence
30 return m + n - 2 * maxLength;
31 }
32};
33
1function minOperations(initial: string, target: string): number {
2 // Get lengths of input strings
3 const initialLength = initial.length;
4 const targetLength = target.length;
5
6 // Create a 2D array to store lengths of common subsequences
7 const dp: number[][] = Array.from({ length: initialLength + 1 }, () => Array(targetLength + 1).fill(0));
8
9 // Variable to track the maximum length of common subsequence found
10 let maxLengthCommonSubseq: number = 0;
11
12 // Loop over each character of initial and target strings
13 for (let i = 1; i <= initialLength; ++i) {
14 for (let j = 1; j <= targetLength; ++j) {
15 // If characters match, increment the length of common subsequence
16 if (initial[i - 1] === target[j - 1]) {
17 dp[i][j] = dp[i - 1][j - 1] + 1;
18 maxLengthCommonSubseq = Math.max(maxLengthCommonSubseq, dp[i][j]);
19 }
20 }
21 }
22
23 // Calculate the minimum number of operations required
24 return initialLength + targetLength - 2 * maxLengthCommonSubseq;
25}
26
Time and Space Complexity
The time complexity is O(m * n)
, as the algorithm processes every element of the initial
string in conjunction with every element of the target
string to compute the longest common subsequence length. This nested iteration results in m * n
operations.
The space complexity is O(m * n)
, since a 2D list f
of size (m+1) * (n+1)
is utilized to store intermediate results of the longest common subsequence computation, where m
and n
are the lengths of the strings initial
and target
, respectively.
Learn more about how to find time and space complexity quickly.
Which of the following shows the order of node visit in a Breadth-first Search?
Recommended Readings
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
https algomonster s3 us east 2 amazonaws com cover_photos stack svg Sliding Window Maximum Monotonic Stack We have an array and a sliding window defined by a start index and an end index The sliding window moves from left of the array to right There are always k elements in
Want a Structured Path to Master System Design Too? Don’t Miss This!