Given strings s1, s2, and s3, find whether s3 is formed by an interleaving of s1 and s2.
An interleaving of two strings s and t is a configuration where s and t are divided into n and msubstrings respectively, such that:
s = s1 + s2 + ... + sn
t = t1 + t2 + ... + tm
|n - m| <= 1
The interleaving is s1 + t1 + s2 + t2 + s3 + t3 + ... or t1 + s1 + t2 + s2 + t3 + s3 + ...
Note:a + b is the concatenation of strings a and b.
Example 1:
Input: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"
Output: true
Explanation: One way to obtain s3 is:
Split s1 into s1 = "aa" + "bc" + "c", and s2 into s2 = "dbbc" + "a".
Interleaving the two splits, we get "aa" + "dbbc" + "bc" + "a" + "c" = "aadbbcbcac".
Since s3 can be obtained by interleaving s1 and s2, we return true.
Example 2:
Input: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc"
Output: false
Explanation: Notice how it is impossible to interleave s2 with any other string to obtain s3.
Example 3:
Input: s1 = "", s2 = "", s3 = ""
Output: true
Constraints:
0 <= s1.length, s2.length <= 100
0 <= s3.length <= 200
s1, s2, and s3 consist of lowercase English letters.
Follow up: Could you solve it using only O(s2.length) additional memory space?
Solutions
Solution 1: Memoization Search
Thinking
An interleaving keeps the relative order of \(s_1\) and \(s_2\), so lengths must match first. Brute force chooses the next char from \(s_1\) or \(s_2\) at every step and explodes.
The state is only “how much of \(s_1\) and \(s_2\) we have used”: \(s_3\)’s index is \(k=i+j\). A choice is legal iff \(s_1[i]\) or \(s_2[j]\) equals \(s_3[k]\). Memoize \((i,j)\) and there are \(O(mn)\) subproblems.
Let's denote the length of string \(s_1\) as \(m\) and the length of string \(s_2\) as \(n\). If \(m + n \neq |s_3|\), then \(s_3\) is definitely not an interleaving string of \(s_1\) and \(s_2\), so we return false.
Next, we design a function \(dfs(i, j)\), which represents whether the remaining part of \(s_3\) can be interleaved from the \(i\)th character of \(s_1\) and the \(j\)th character of \(s_2\). The answer is \(dfs(0, 0)\).
The calculation process of function \(dfs(i, j)\) is as follows:
If \(i \geq m\) and \(j \geq n\), it means that both \(s_1\) and \(s_2\) have been traversed, so we return true.
If \(i < m\) and \(s_1[i] = s_3[i + j]\), it means that the character \(s_1[i]\) is part of \(s_3[i + j]\). Therefore, we recursively call \(dfs(i + 1, j)\) to check whether the next character of \(s_1\) can match the current character of \(s_2\). If it can match, we return true.
Similarly, if \(j < n\) and \(s_2[j] = s_3[i + j]\), it means that the character \(s_2[j]\) is part of \(s_3[i + j]\). Therefore, we recursively call \(dfs(i, j + 1)\) to check whether the next character of \(s_2\) can match the current character of \(s_1\). If it can match, we return true.
Otherwise, we return false.
To avoid repeated calculations, we can use memoization search.
The time complexity is \(O(m \times n)\), and the space complexity is \(O(m \times n)\). Here, \(m\) and \(n\) are the lengths of strings \(s_1\) and \(s_2\) respectively.
implSolution{#[allow(dead_code)]pubfnis_interleave(s1:String,s2:String,s3:String)->bool{letn=s1.len();letm=s2.len();ifs1.len()+s2.len()!=s3.len(){returnfalse;}letmutrecord=vec![vec![-1;m+1];n+1];Self::dfs(&mutrecord,n,m,0,0,&s1.chars().collect(),&s2.chars().collect(),&s3.chars().collect(),)}#[allow(dead_code)]fndfs(record:&mutVec<Vec<i32>>,n:usize,m:usize,i:usize,j:usize,s1:&Vec<char>,s2:&Vec<char>,s3:&Vec<char>,)->bool{ifi>=n&&j>=m{returntrue;}ifrecord[i][j]!=-1{returnrecord[i][j]==1;}// Set the initial valuerecord[i][j]=0;letk=i+j;// Let's try `s1` firstifi<n&&s1[i]==s3[k]&&Self::dfs(record,n,m,i+1,j,s1,s2,s3){record[i][j]=1;}// If the first approach does not succeed, let's then try `s2`ifrecord[i][j]==0&&j<m&&s2[j]==s3[k]&&Self::dfs(record,n,m,i,j+1,s1,s2,s3){record[i][j]=1;}record[i][j]==1}}
Solution 1 already has the transition. Bottom-up: \(f[i][j]\) means the first \(i\) chars of \(s_1\) and first \(j\) of \(s_2\) can form the first \(i+j\) of \(s_3\). Empty-empty is true; fill by increasing \(i,j\) and drop the recursion stack.
We can convert the memoization search in Solution 1 into dynamic programming.
We define \(f[i][j]\) to represent whether the first \(i\) characters of string \(s_1\) and the first \(j\) characters of string \(s_2\) can interleave to form the first \(i + j\) characters of string \(s_3\). When transitioning states, we can consider whether the current character is obtained from the last character of \(s_1\) or the last character of \(s_2\). Therefore, we have the state transition equation:
where \(f[0][0] = \textit{true}\) indicates that an empty string is an interleaving string of two empty strings.
The answer is \(f[m][n]\).
The time complexity is \(O(m \times n)\), and the space complexity is \(O(m \times n)\). Here, \(m\) and \(n\) are the lengths of strings \(s_1\) and \(s_2\) respectively.
The follow-up asks for \(O(|s_2|)\) extra space. \(f[i][j]\) depends only on the previous row and the left cell, so one rolling array is enough — the \(O(n)\) bound in the problem.
We notice that \(f[i][j]\) is only related to \(f[i - 1][j]\) and \(f[i][j - 1]\). Therefore, we can use a rolling array to reduce the space complexity to \(O(n)\). The time complexity remains \(O(m \times n)\).