Interleaving String
Medium · 2-D Dynamic Programming · Explanation and code example
Dynamic Programming (Optimal)
How it works
We want to check if s3 can be formed by interleaving s1 and s2 while keeping the order of characters from both strings.
A common DP idea is:
- at positions
(i, j), we have usedicharacters froms1andjcharacters froms2 - so the next character we must match in
s3is at indexi + j
From this state, we can move forward in two ways:
- take
s1[i]if it matchess3[i + j] - take
s2[j]if it matchess3[i + j]
The 2D DP solution stores this for every (i, j), but we can do better:
- each DP row only depends on the row below and the current row being built
- so we can reuse a single 1D array
- and instead of building a separate
nextarray, we can update the 1D array in-place using one extra variable that tracks the “right neighbor” value
We also swap strings so that s2 is the longer one, keeping the DP array as small as possible.
Steps
- Let
m = len(s1)andn = len(s2). - If
m + n != len(s3), returnfalseimmediately. - If
s2is shorter thans1, swap them so the DP array size becomesO(min(m, n)). - Create a boolean array
dpof sizen + 1:dp[j]represents whethers3[i + j:]can be formed usings1[i:]ands2[j:]for the currenti
- Initialize the base case:
- set
dp[n] = true(when both suffixes are empty)
- set
- Iterate
ifrommdown to0:- keep a variable
nextDpthat represents the value to the right (dp[j + 1]) for the current row - initialize it as
trueonly wheni == m(bottom row base case)
- keep a variable
- Iterate
jfromndown to0:- compute whether the state
(i, j)is valid:- it is valid if taking from
s1matches and the state below (dp[j]) was valid - or taking from
s2matches and the state to the right (nextDp) is valid
- it is valid if taking from
- write the result back into
dp[j](in-place update) - update
nextDpto the newdp[j]for the next iteration to the left
- compute whether the state
- After all updates,
dp[0]tells whethers3can be formed starting from the beginning of both strings. - Return
dp[0]
Complexity
- Time complexity:
O(m * n) - Space complexity:
O(min(m, n))
Where
mis the length of the strings1andnis the length of the strings2.
Things to watch for
Skipping the Length Check
A quick optimization that many solutions miss is checking if len(s1) + len(s2) == len(s3) upfront. If the lengths do not match, interleaving is impossible regardless of the characters. Skipping this check leads to unnecessary computation and can cause index out-of-bounds errors in some implementations.
Confusing Position Tracking with Three Indices
Since k = i + j always holds (where k is position in s3, i in s1, j in s2), you only need to track two indices. Some implementations incorrectly track all three independently, leading to inconsistent states or missed memoization opportunities. The key insight is that knowing positions in s1 and s2 uniquely determines the position in s3.
Greedy Character Matching
When both s1[i] and s2[j] match s3[k], you cannot greedily choose one over the other. Both branches must be explored. A common bug is to always prefer taking from s1 (or s2) when both match, which fails for cases like s1 = "a", s2 = "a", s3 = "aa" where either order works but a greedy approach might get stuck.
Python code example
class Solution:
def isInterleave(self, s1: str, s2: str, s3: str) -> bool:
m, n = len(s1), len(s2)
if m + n != len(s3):
return False
if n < m:
s1, s2 = s2, s1
m, n = n, m
dp = [False for _ in range(n + 1)]
dp[n] = True
for i in range(m, -1, -1):
nextDp = True if i == m else False
for j in range(n, -1, -1):
res = False if j < n else nextDp
if i < m and s1[i] == s3[i + j] and dp[j]:
res = True
if j < n and s2[j] == s3[i + j] and nextDp:
res = True
dp[j] = res
nextDp = dp[j]
return dp[0]
C++ code example
class Solution {
public:
bool isInterleave(string s1, string s2, string s3) {
int m = s1.size(), n = s2.size();
if (m + n != s3.size()) return false;
if (n < m) {
swap(s1, s2);
swap(m, n);
}
vector<bool> dp(n + 1, false);
dp[n] = true;
for (int i = m; i >= 0; i--) {
bool nextDp = (i == m ? true : false);
for (int j = n; j >= 0; j--) {
bool res = (j < n ? false : nextDp);
if (i < m && s1[i] == s3[i + j] && dp[j]) {
res = true;
}
if (j < n && s2[j] == s3[i + j] && nextDp) {
res = true;
}
dp[j] = res;
nextDp = dp[j];
}
}
return dp[0];
}
};
Source and license
Adapted from NeetCode’s source · MIT license. Node types are supplied by LeetCode where needed.