97. Interleaving String
- Goal: Determine if string
s3is formed by an interleaving of stringss1ands2. > Interleaving: Combining two strings by merging their characters while preserving the original order of characters from each string. - Constraints:
0 <= s1.length, s2.length <= 1000 <= s3.length <= 200- All strings consist of lowercase English letters.
- Follow-up: Can you solve it using only O(s2.length) additional memory space?
Understanding the Problem
Given three strings s1, s2, and
s3, we need to check if s3 can be formed by
interleaving s1 and s2. An
interleaving means that we can shuffle s1
and s2 together, maintaining the order of characters in
each string, to get s3.
Key Points:
- Order Preservation: The order of characters in
s1ands2must be maintained ins3. - Interleaving: At each step, you can choose a
character from either
s1ors2.
Example
1 | s1 = "aabcc" |
output:
true
Quick Steps for Solving the Problem
1. Check Lengths
- Condition: If
s1.length + s2.length != s3.length, returnfalse. - Rationale: If the total length doesn't match, it's
impossible for
s3to be an interleaving ofs1ands2.
2. Initialise a 2D DP Table
- Purpose: Use Dynamic Programming to store subproblem results.
- Structure: Create a 2D array
dpof size(s1.length + 1) x (s2.length + 1). - Base Case: Set
dp[0][0] = true(empty strings can form an empty string).
3. Fill the First Row and First Column
- First Column (
dp[i][0]):dp[i][0] = dp[i - 1][0] && s1[i - 1] == s3[i - 1]- Meaning: Can
s3up to lengthibe formed bys1up to lengthi?
- First Row (
dp[0][j]):dp[0][j] = dp[0][j - 1] && s2[j - 1] == s3[j - 1]- Meaning: Can
s3up to lengthjbe formed bys2up to lengthj?
4. Fill the Rest of the DP Table
- For Each Cell
dp[i][j]:- Option 1: If
dp[i - 1][j]istrueands1[i - 1] == s3[i + j - 1], thendp[i][j] = true.- Meaning: If we can form
s3up toi + j - 1by addings1[i - 1].
- Meaning: If we can form
- Option 2: If
dp[i][j - 1]istrueands2[j - 1] == s3[i + j - 1], thendp[i][j] = true.- Meaning: If we can form
s3up toi + j - 1by addings2[j - 1].
- Meaning: If we can form
- Else:
dp[i][j] = false.
- Option 1: If
5. Return the Result
- Answer:
dp[s1.length][s2.length]- Meaning: Whether
s3can be formed by interleavings1ands2.
- Meaning: Whether
Analogy to Simplify Understanding
Imagine you have two queues of characters:
- Queue A (
s1): A line of people waiting in order. - Queue B (
s2): Another line of people waiting in order. - Goal: Form a new line (
s3) by merging these two queues, taking one person at a time from either queue, without changing the order in each queue.
Rules:
- You can pick the next person from either Queue A or Queue B.
- You cannot skip or rearrange people within a queue.
- The final line (
s3) must have everyone from both queues, in some order that respects the above rules.
Step-by-Step Implementation
1. Check Total Length
1 | if (s1.length + s2.length !== s3.length) return false; |
2. Initialise DP Table
1 | const N = s1.length; |
3. Fill First Row and First Column
Fill First Column
(dp[i][0])
1 | for (let i = 1; i <= N; i++) { |
Fill First Row (dp[0][j])
1 | for (let j = 1; j <= M; j++) { |
4. Fill the Rest of the DP Table
1 | for (let i = 1; i <= N; i++) { |
5. Return the Result
1 | return dp[N][M]; |
JavaScript Implementation
1 | /** |
Detailed Explanation
Understanding the DP Table
- Dimensions:
(s1.length + 1) x (s2.length + 1) dp[i][j]Meaning: Whethers3up to lengthi + jcan be formed by interleavings1up to lengthiands2up to lengthj.
Base Cases
dp[0][0] = true: Emptys1ands2can form emptys3.- First Row and Column:
- We check if
s3up to lengthiorjmatchess1ors2respectively.
- We check if
Filling the DP Table
At each cell dp[i][j], we consider two
possibilities:
- Taking a character from
s1:- If
dp[i - 1][j]istrue(we can forms3up toi + j - 2withs1[0..i-2]ands2[0..j-1]). - And if
s1[i - 1]matchess3[i + j - 1].
- If
- Taking a character from
s2:- If
dp[i][j - 1]istrue(we can forms3up toi + j - 2withs1[0..i-1]ands2[0..j-2]). - And if
s2[j - 1]matchess3[i + j - 1].
- If
If either possibility is true, we set
dp[i][j] = true.
Example Walkthrough
Let's consider s1 = "abc", s2 = "def",
s3 = "adbcef".
- dp[1][1]:
dp[0][1]isfalse, so first condition fails.dp[1][0]istrueands1[0] == s3[1]('a' == 'd') isfalse.dp[1][1]remainsfalse.
- dp[1][2]:
- Similar checks,
dp[1][2]remainsfalse.
- Similar checks,
- dp[2][1]:
dp[1][1]isfalse.dp[2][0]isfalse.dp[2][1]remainsfalse.
- dp[3][3]:
dp[2][3]isfalse.dp[3][2]istrueands2[2] == s3[5]('f' == 'f') istrue.- So,
dp[3][3] = true.
Final Result: Since dp[3][3] = true,
s3 is an interleaving of s1 and
s2.
Summary
- Dynamic Programming: We use a 2D DP table to keep
track of possible interleavings up to each point in
s1ands2. - Key Concepts:
- Preserving Order: We must maintain the sequence of
characters in
s1ands2. - State Transition: Each state depends on previous
states, considering characters from
s1ands2.
- Preserving Order: We must maintain the sequence of
characters in
- Analogy: Merging two queues without changing the order within each queue.
- Time Complexity: O(N * M)
- Space Complexity: O(N * M), can be optimised to O(M)
Further Understanding and Tips
Why Use dp[i][j]?
dp[i][j]represents a subproblem: Whethers3up to lengthi + jcan be formed by interleavings1up to lengthiands2up to lengthj.- Breaking Down the Problem: By solving smaller subproblems, we build up to the final solution.
Handling Edge Cases
- Empty Strings: If
s1ors2is empty, we need to check ifs3matches the other string. - Initialization: Properly initializing the first row and column is crucial for these cases.
Visualization
- DP Table as a Grid: Visualise the DP table as a grid where each cell represents a state.
- Path Finding: Finding a path from
dp[0][0]todp[N][M]corresponds to interleavings1ands2to forms3.
Practice Problems for 2D Dynamic Programming
To strengthen your understanding of 2D DP, consider practicing the following problems:
- LeetCode 62: Unique Paths
- LeetCode 1143: Longest Common Subsequence
- LeetCode 221: Maximal Square
- LeetCode 72: Edit Distance
Final Thoughts
Dynamic Programming can be challenging, but breaking down the problem into subproblems and understanding the state transitions makes it manageable. Remember to:
- Define the DP State Clearly: Know what each element in your DP table represents.
- Initialise Properly: Base cases are crucial for correct results.
- Think Recursively: How can you build the current state from previous states?
- Practice: The more problems you solve, the better you'll understand DP patterns.
Happy coding!