Print All Interleavings Of Given Two Strings In C++

In this article, we will discuss how to print all interleavings of the given two strings in C++. But before going through its implementation, we will know about the interleavings.

What are Interleavings?

Interleavings of two strings are formed by merging the characters of two strings in all possible ways while maintaining their relative order. For example, the interleavings of "AB" and "CD" are "ABCD", "ACBD", "ACDB", "CABD", "CADB", and "CDAB".

The problem of printing all possible interleavings of two given strings is a common recursion and dynamic programming problem.

Problem Statement

Given two strings, str1 and str2 , print all possible interleavings of the two strings. An interleaved string preserves the order of characters in individual strings.

For example:

str1 = "AB"

str2 = "CD"

Possible Interleavings:

1. Naive Recursive Approach

A simple recursive solution is to generate all interleavings by choosing a character from either string at each step.

  • If str1 is non-empty, recursively generate interleavings for str1[1...] and str2 . Prepend str1[0] to result.
  • If str2 is non-empty, recursively generate interleavings for str1 and str2[1...] . Prepend str2[0] to result.

Base cases:

  • If both strings are empty, print the generated interleaving.
  • If one string is empty, only choose characters from the non-empty string.

It gives us an exponential time complexity of O(2^n) , where n is the length of the two strings.

Example:

Let's take an example to print all interleavings of given two strings in C++.

Example

#include <iostream>
using namespace std;

// Recursive Function to print interleavings 
void interleave(string s1, string s2, string res) {

 // Base cases
 if (s1.empty() && s2.empty()) {
 cout << res << endl;
 return; 
 }
 
 if (!s1.empty()) {
 interleave(s1.substr(1), s2, res + s1[0]); 
 }
 
 if (!s2.empty()) {
 interleave(s1, s2.substr(1), res + s2[0]);
 }
}

int main() {

 string str1 = "AB";
 string str2 = "CD";
 
 interleave(str1, str2, "");

 return 0;
}

Output:

Output

ABCD
ACBD
ACDB
CABD
CADB
CDAB

This recursively generates all valid interleavings in lexicographic order.

2. Dynamic Programming Approach

We can optimize it using a bottom-up dynamic programming approach.

  • Create a 2D matrix dpm+1 to store the interleavings.
  • Fill up the matrix diagonally from empty strings to full strings.
  • To fill a cell dpi: Appendstr1[i-1] to interleavings of dpi-1 Appendstr2[j-1] to interleavings of dpi
  • Final interleavings will be stored in dpm.
  • It reduces the complexity to O(m*n) as we store the results instead of recomputing.
  • Appendstr1[i-1] to interleavings of dpi-1
  • Appendstr2[j-1] to interleavings of dpi

Example:

C++ code implementing Dynamic programming approach:

Example

#include <iostream>
#include <vector>
using namespace std;

//Function to print interleavings using DP
void printInterleavings(string s1, string s2){
 
 int m = s1.length();
 int n = s2.length();
 
 // dp table to store interleavings
 vector<vector<vector<string>>> dp(m+1, vector<vector<string>>(n+1)); 
 
 // Base cases
 for(int i=0; i<=m; i++){
 dp[i][0] = {s1.substr(0, i)}; 
 }
 
 for(int j=0; j<=n; j++){
 dp[0][j] = {s2.substr(0, j)};
 }

 // Build interleavings from smaller to larger strings 
 for(int i=1; i<=m; i++){
 for(int j=1; j<=n; j++){
 
 // Append s1[i-1] to previous interleavings
 for(string s: dp[i-1][j]){
 s.push_back(s1[i-1]);
 dp[i][j].push_back(s);
 }
 
 // Append s2[j-1] to previous interleavings 
 for(string s: dp[i][j-1]){
 s.push_back(s2[j-1]);
 dp[i][j].push_back(s);
 }
 }
 }
 
 // Print all interleavings
 for(string s: dp[m][n]){
 cout << s << "\n";
 }
}

int main() {

 string s1 = "AB";
 string s2 = "CD";

 printInterleavings(s1, s2);

 return 0;
}

Output:

Output

ABCD
ACBD
ACDB 
CABD
CADB
CDAB

This bottom-up DP approach avoids recomputation and improves efficiency over the recursive approach.

Input Required

This code uses input(). Please provide values below: