Open In App

Check if a String is Interleaving of Other Two

Last Updated : 25 Mar, 2025
Summarize
Comments
Improve
Suggest changes
Like Article
Like
Share
Report
News Follow

Give three strings s1, s2 and s3, determine if s3 is formed by interleaving s1 and s2.
A string s3 is an interleaving of s1 and s2 if:

  • It contains all characters of s1 and s2 while preserving their relative order.
  • Characters from s1 and s2 appear in s3 in the same order as in their original strings.
  • The length of s3 equals the combined length of s1 and s2.

Example

Input: s1 = “AAB”, s2 = “AAC”, s3 = “AAAABC”,
Output: true
Explanation: The string “AAAABC” has all characters of the other two strings and in the same order.

Input: s1 = “AB”, s2 = “C”, s3 = “ACB”,
Output: true
Explanation: s3 has all characters of s1 and s2 and retains order of characters of s1.

Input: s1 = “YX”, s2 = “X”, s3 = “XXY”
Output: false
Explanation: “XXY ” is not interleaved of “YX” and “X”. The strings that can be formed are YXX and XYX

[Naive Approach] Using Recursion – O(2^(m+n)) Time and O(m+n) Space

The main idea is to recursively check whether s3 can be formed by choosing characters from s1 and s2, ensuring we maintain the correct order. To do so, initialise three variable i, j, and k to store the index of string s1, s2, and s3 respectively. To handle all cases, the following possibilities need to be considered.

  • If the first character of s3 matches the first character of s1, we move one character ahead in s1 and s3 and recursively check.
  • If the first character of s3 matches the first character of s2, we move one character ahead in s2 and s3 and recursively check.
  • If none of the two characters match, return false.
  • If any of the above function returns true or s1, s2 and s3 are empty then return true else return false.
C++
// Cpp program to check if Check if a String is
// Interleaving of Other Two using recursion
#include <iostream>
using namespace std;

// i amnd j are indexes in s1 and s2 respectively
bool isILRec(string &s1, string &s2, string &s3, int i, int j)
{

    int k = i + j;

    // If all strings are fully traversed
    if (i == s1.size() && j == s2.size() && k == s3.size())
        return true;

    // If any of the above mentioned two
    // possibilities then return true
    // otherwise return false.
    bool a = (i < s1.size()) && (s3[k] == s1[i]) && isILRec(s1, s2, s3, i + 1, j);
    bool b = (j < s2.size()) && (s3[k] == s2[j]) && isILRec(s1, s2, s3, i, j + 1);

    return a || b;
}

bool isInterleave(string &s1, string &s2, string &s3)
{

    // A basic condition that must be ensured
    if (s1.size() + s2.size() != s3.size())
        return false;

    return isILRec(s1, s2, s3, 0, 0);
}

int main()
{
    string s1 = "AAB";
    string s2 = "AAC";
    string s3 = "AAAABC";
    cout << (isInterleave(s1, s2, s3) ? "true" : "false") << endl;
    return 0;
}
Java Python C# JavaScript

Output
true

[Better Approach 1] Using Top-Down DP – O(m*n) Time and O(n*m) Space

The above recursive solution certainly has many overlapping sub-problems. For example, if we consider s1 = “AAB”, s2 = “AAC” and s3 = “AAAABC” and draw a recursion tree, there will be many overlapping subproblems. Therefore, like any other typical Dynamic Programming problems, we can solve it by creating a table and store results of sub-problems. Below is a memoization based solution that creates a 2D memo array.

We optimize the recursive approach using memoization to avoid redundant calculations.

We track indices i and j in s1 and s2, respectively, ensuring that characters match s3 at index k = i + j. If either s1[i] or s2[j] matches s3[k], we recursively move forward while storing results in a memo table to prevent recomputation.

The base case ensures that when all strings are fully traversed, we return true. This approach significantly reduces the time complexity compared to plain recursion.

Steps to implement the above idea:

  • Check if s3 length is equal to s1 + s2, return false if not.
  • Create a (m+1) x (n+1) table memo[][] with -1 to store results. The value of memo[i][j] is going to be 1 if s3[0..i + j-1] is an interleaving of s1[0..i-1] and s2[0..j-1], else 0.
  • If memo[i][j] is not -1, return the stored result to avoid recomputation.
  • Try both possibilities (matching s1[i] or s2[j] with s3[k]) and store the result in memo[i][j].
  • Return true if all strings are fully traversed, otherwise return false.
C++
// C++ program to check if Check if a String is
// Interleaving of Other Two using memoization
#include <bits/stdc++.h>
using namespace std;

bool isILRec(string &s1, string &s2, string &s3, int i, int j, vector<vector<int>> &memo)
{
    int k = i + j;
    int m = s1.size(), n = s2.size();

    // Base case: If all strings are fully traversed
    if (i == m && j == n && k == s3.size())
        return true;

    // If we have already solved this subproblem,
    // return the stored result
    if (memo[i][j] != -1)
    {
        return memo[i][j];
    }

    // Check if next characcter of s1 maatchs with
    // with the next one of s3
    bool a = (i < m && s1[i] == s3[k]) && isILRec(s1, s2, s3, i + 1, j, memo);

    // Check if next characcter of s2 maatchs with
    // with the next one of s3
    bool b = (j < n && s2[j] == s3[k]) && isILRec(s1, s2, s3, i, j + 1, memo);

    return memo[i][j] = a || b;
}

bool isInterleave(string &s1, string &s2, string &s3)
{

    int m = s1.size(), n = s2.size();

    // A basic condition that must be ensured
    if (m + n != s3.size())
        return false;

    // Intiialize memo with -1 and make Recursive function call
    vector<vector<int>> memo(m + 1, vector<int>(n + 1, -1));
    return isILRec(s1, s2, s3, 0, 0, memo);
}

int main()
{
    string s1 = "AAB";
    string s2 = "AAC";
    string s3 = "AAAABC";
    cout << (isInterleave(s1, s2, s3) ? "true" : "false") << endl;
    return 0;
}
Java Python C# JavaScript

Output
true

[Better Approach 2] Using Bottom-Up DP – O(m*n) Time and O(m*n) Space

Step by Step implementation:

  • The main idea is to create a 2D DP table dp[m+1][n+1] , where dp[i][j] represents whether s3[0…i+j-1] is an interleaving of s1[0…i-1] and s2[0…j-1].
  • dp[0][0] = true, because an empty s1 and s2 can form an empty s3.
  • Fill the First Row and First Column
    • If s1 is empty, we can only form s3 by taking characters from s2.
    • If s2 is empty, we can only form s3 by taking characters from s1.
  • The DP formula checks whether s3[k] (where k = i + j) matches either:
    • The current character of s1 (s1[i-1]), while ensuring that the previous part dp[i-1][j] is true.
    • The current character of s2 (s2[j-1]), while ensuring that the previous part dp[i][j-1] is true.
  • The DP table is filled iteratively until dp[m][n] contains the answer.
C++
// C++ program to check if Check if a String is Interleaving
// of Other Two using Dynamic Programming
#include <bits/stdc++.h>
using namespace std;

// The main function that
// returns true if s3 is
// an interleaving of s1
// and s2, otherwise false.
bool isInterleave(string &s1, string &s2, string &s3)
{

    int m = s1.size(), n = s2.size();

    // s3 can be an interleaving of s1 and s2 only if
    // the sum of lengths of s1 & s2 is equal to the length of s3.
    if (m + n != s3.size())
        return false;

    vector<vector<bool>> dp(m + 1, vector<bool>(n + 1, false));

    // Handle the corner case where both s1 and s2 are empty
    dp[0][0] = true;

    // Fill the first row (when s1 is empty)
    for (int j = 1; j <= n; ++j)
    {
        dp[0][j] = (s2[j - 1] == s3[j - 1]) && dp[0][j - 1];
    }

    // Fill the first column (when s2 is empty)
    for (int i = 1; i <= m; ++i)
    {
        dp[i][0] = (s1[i - 1] == s3[i - 1]) && dp[i - 1][0];
    }

    // Process all characters of s1 and s2
    for (int i = 1; i <= m; ++i)
    {
        for (int j = 1; j <= n; ++j)
        {
            int k = i + j;
            dp[i][j] = (s1[i - 1] == s3[k - 1] && dp[i - 1][j]) || (s2[j - 1] == s3[k - 1] && dp[i][j - 1]);
        }
    }

    return dp[m][n];
}

int main()
{
    string s1 = "AAB";
    string s2 = "AAC";
    string s3 = "AAAABC";
    cout << (isInterleave(s1, s2, s3) ? "true" : "false") << endl;
    return 0;
}
Java Python C# JavaScript

Output
true

[Expected Approach] Space-Optimized DP – O(n * m) Time and O(m) Space

The idea is to optimize DP space by using two 1D arrays instead of a 2D table.

We check if s3 can be formed by interleaving s1 and s2 while maintaining relative order. prev stores results of the previous row, while cur stores the current row values.

We fill the first row for cases where s1 is empty, then iterate through both strings, updating cur[j] based on matches with s3. After processing a row, prev is updated with cur, ensuring space efficiency.

Steps to implement the above idea:

  • Check if the total length matches, return false if not.
  • Initialize two arrays prev and cur to store previous and current results.
  • Set the base case where an empty prefix is valid.
  • Fill the first row based on matches with s2.
  • Iterate through both s1 and s2, updating cur based on matches.
  • Return cur[m], indicating interleaving validity.


C++
// CPP program to check if a string is
// interleaving of other two strings
#include <bits/stdc++.h>
using namespace std;

bool isInterleave(string &s1, string &s2, string &s3)
{

    // return false is length of s3 is
    // not equal to sum of lengths of s1 and s2
    if (s1.size() + s2.size() != s3.size())
        return false;

    int n = s1.size(), m = s2.size();

    // Create two arrays prev and cur to store
    // results of previous and current states
    vector<int> prev(m + 1), cur(m + 1);

    // set empty strings as true
    prev[0] = true;

    // Fill the first row (when s1 is empty)
    for (int j = 1; j <= m; ++j)
    {
        prev[j] = (s2[j - 1] == s3[j - 1]) && prev[j - 1];
    }

    // Process all characters of s1 and s2
    for (int i = 1; i <= n; ++i)
    {

        // fill the first column of current row
        cur[0] = (s1[i - 1] == s3[i - 1]) && prev[0];
        for (int j = 1; j <= m; ++j)
        {
            int k = i + j;
            cur[j] = (s1[i - 1] == s3[k - 1] && prev[j]) || (s2[j - 1] == s3[k - 1] && cur[j - 1]);
        }

        // store current row in previous row
        prev = cur;
    }

    return cur[m];
}

int main()
{
    string s1 = "AAB";
    string s2 = "AAC";
    string s3 = "AAAABC";
    cout << (isInterleave(s1, s2, s3) ? "true" : "false");
    return 0;
}
Java Python C# JavaScript

Output
true


Next Article

Similar Reads

three90RightbarBannerImg