Print all interleavings of given two strings
Given two strings str1 and str2, write a function that prints all interleavings of the given two strings. You may assume that all characters in both strings are different
Example:
Input: str1 = "AB", str2 = "CD"
Output:
ABCD
ACBD
ACDB
CABD
CADB
CDAB
Input: str1 = "AB", str2 = "C"
Output:
ABC
ACB
CAB
An interleaved string of given two strings preserves the order of characters in individual strings. For example, in all the interleavings of above first example, ‘A’ comes before ‘B’ and ‘C’ comes before ‘D’.
Let the length of str1 be m and the length of str2 be n. Let us assume that all characters in str1 and str2 are different. Let count(m, n) be the count of all interleaved strings in such strings. The value of count(m, n) can be written as following.
count(m, n) = count(m-1, n) + count(m, n-1)
count(1, 0) = 1 and count(0, 1) = 1
To print all interleavings, we can first fix the first character of str1[0..m-1] in output string, and recursively call for str1[1..m-1] and str2[0..n-1]. And then we can fix the first character of str2[0..n-1] and recursively call for str1[0..m-1] and str2[1..n-1]. Thanks to akash01 for providing following C implementation.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// The main function that recursively prints all interleavings. The variable
// iStr is used to store all interleavings (or output strings) one by one.
// i is used to pass next available place in iStr
void printIlsRecur (char *str1, char *str2, char *iStr, int m, int n, int i)
{
// Base case: If all characters of str1 and str2 have been included in
// output string, then print the output string
if ( m==0 && n ==0 )
{
printf("%s\n", iStr) ;
}
// If some characters of str1 are left to be included, then include the
// first character from the remaining characters and recur for rest
if ( m != 0 )
{
iStr[i] = str1[0];
printIlsRecur (str1 + 1, str2, iStr, m-1, n, i+1);
}
// If some characters of str2 are left to be included, then include the
// first character from the remaining characters and recur for rest
if ( n != 0 )
{
iStr[i] = str2[0];
printIlsRecur (str1, str2+1, iStr, m, n-1, i+1);
}
}
// Allocates memory for output string and uses printIlsRecur()
// for printing all interleavings
void printIls (char *str1, char *str2, int m, int n)
{
// allocate memory for the output string
char *iStr= (char*)malloc((m+n+1)*sizeof(char));
// Set the terminator for the output string
iStr[m+n] = '\0';
// print all interleavings using printIlsRecur()
printIlsRecur (str1, str2, iStr, m, n, 0);
// free memory to avoid memory leak
free(iStr);
}
// Driver program to test above functions
int main()
{
char *str1 = "AB";
char *str2 = "CD";
printIls (str1, str2, strlen(str1), strlen(str2));
return 0;
}
Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.
You may also like following posts
Incidentally, the count(m, n) function is very similar to Pascal Triangle's formula, and so related to the binomial coefficient. A closed form is (m+n)!/(n!m!).
Working out the exact number of unique strings is harder, as the exact position of identical characters changes the result (for instance, ab ac have 4 unique interleaved strings, but ab ca have 5).
#include<stdio.h> #include<string.h> void printallinterleaving(char *a,char *b,char *c,int alen,int blen,int i) { if(!*a && !*b) { c[i]='\0'; printf("%s\n",c); return ; } if(*a) { c[i]=*a; printallinterleaving(a+1,b,c,alen-1,blen,i+1); } if(*b) { c[i]=*b; printallinterleaving(a,b+1,c,alen,blen-1,i+1); } } int main() { char a[10],b[10],c[20]; scanf("%s%s",a,b); printallinterleaving(a,b,c,strlen(a),strlen(b),0); return 0; }Nice .. ur code omits the use of parameters alen blen
#include<stdio.h> #include<string.h> void printallinterleaving(char *a,char *b,char *c,int i) { if(!*a && !*b) { c[i]='\0'; printf("%s\n",c); return ; } if(*a) { c[i]=*a; printallinterleaving(a+1,b,c,i+1); } if(*b) { c[i]=*b; printallinterleaving(a,b+1,c,i+1); } } int main() { char a[10],b[10],c[20]; scanf("%s%s",a,b); printallinterleaving(a,b,c,0); return 0; }Variation 1 : Print unique interleaved strings.
Can we do better than generating all of them ( and then removing duplicates ) ?
That's an interesting question. I'm not sure about my code; but my approach is to try to detect situations where selecting from a then from b would lead to the same result as selecting from b then from a, and prevent the second option from being followed.
#include<stdio.h> #include<string.h> void printallinterleaving(char *a,char *b,char *c,int i, int not_a) { if(!*a && !*b) { c[i]='\0'; printf("%s\n",c); return ; } if(!not_a && *a) { c[i]=*a; printallinterleaving(a+1,b,c,i+1, 0); } if(*b) { c[i]=*b; printallinterleaving(a,b+1,c,i+1, *a==*b); } } int main(int argc, char *argv[]) { char c[strlen(argv[1])+strlen(argv[2])+1]; printallinterleaving(argv[1],argv[2],c,0, 0); return 0; }$ ./interleave ab ac
gives
abac
aabc
aacb
acab
instead of 6 strings.
I tried other combinations and they all seem to work, but I can't think of a way to prove the result is correct.
fails for "ab" & "ab"
Yes, I didn't think of that, and it's a good example.
I guess this shows there is no algorithm that can detect repetition in constant space, so there is little choice but to collect the output and check for duplicates.
Actually, there is a way to detect repetitions due to common subsequences: the algorithm has a A branch (adding characters from a) and a B branch (adding characters from b); in the B branch, we can detect common subsequences, and make sure that we do not add characters that repeat a pattern already generated in the A branch. By keeping track of how many characters are added from either a or b, and making sure that those coming from a are always less than those coming from b, then there would be no repetition. If we add more characters from b than there were in the common subsequence, there can be no repetition either and we are "out" out the subsequence.
So if we detect a common subsequence of cp characters, and add cp_b from b, then we can add at most cp_a cp from b, we have left the common subsequence and can ignore possible repetitions.
The code below implements these ideas, and appears to work on most data I tried it on.
What it does not handle properly, is repeating characters (for instance aa and ab have 3 unique interleaved strings, but the algorithm generates 4). Not quite sure how to tackle that problem (but it should be possible to extend the current version to handle that case as well).
#include<stdio.h> #include<string.h> void printallinterleaving(char *a,char *b,char *c,int i, int cp, int cp_a, int cp_b) { if(!*a && !*b) { c[i]='\0'; printf("%s\n",c); return ; } if((!cp || (cp_a < cp_b - 1)) && *a) { /* first branch; do not update variables in place to avoid clobbering second branch */ c[i]=*a; printallinterleaving(a+1,b,c,i+1, cp, cp ? cp_a + 1: 0, cp_b); } if(*b) { /* no more branching after this, so we can update variables in place */ if (!cp) { cp_a = 0; cp_b = 0; /* compute length of common prefix */ char *aa = a, *bb = b; while (*aa && *aa++==*bb++) cp++; } if (cp) cp_b++; if (cp_b > cp) cp = cp_a = cp_b = 0; c[i]=*b; printallinterleaving(a,b+1,c,i+1, cp, cp_a, cp_b); } } int main(int argc, char *argv[]) { char c[strlen(argv[1])+strlen(argv[2])+1]; printallinterleaving(argv[1],argv[2],c,0, 0, 0, 0); return 0; }... aba bab also has repetition (only one, though).
A tough one to crack...
A different solution in Java.
public class Interleaving { public static void main(String[] args) throws Exception { new Interleaving().generateInterleavings("ABC", "1234", System.out); } public void generateInterleavings(String s1, String s2, Appendable target) throws Exception { interleavingsInternal(s1, s2, target); interleavingsInternal(s2, s1, target); } private void interleavingsInternal(String s1, String s2, Appendable target) throws Exception { target.append(s1).append(s2).append("\n"); for (int i1 = 0; i1 < s1.length() - 1; i1++) { CharSequence prefix = s1.subSequence(0, i1 + 1); for(int i2 = 0; i2 < s2.length(); i2++) { target.append(prefix) .append(s2, 0, i2 + 1) .append(s1, i1 + 1, s1.length()) .append(s2, i2 + 1, s2.length()) .append("\n"); } } } }It's inefficient isn't it?
Why not just use combination to decide the positions for one string in the output string?
@cotaku39: Could you please provide more details of your approach?
After contrast I found my way using combination is slower than the one posted above...
#include<iostream> #include<string.h> void PrintAllInterLeaving(char *str1, int stIndex1, int len1, char *str2, int stIndex2, int len2, char *output, int curIndex) { if(len1 == stIndex1) { std::cout<<"\n"; for(int index = 0; index < curIndex; index++) { std::cout<<output[index]; } for(int index = stIndex2; index < len2; index++) { std::cout<<str2[index]; } } else if (len2 == stIndex2) { std::cout<<"\n"; for(int index = 0; index < curIndex; index++) { std::cout<<output[index]; } for(int index = stIndex1; index < len1; index++) { std::cout<<str1[index]; } } else { output[curIndex] = str1[stIndex1]; PrintAllInterLeaving(str1, stIndex1 +1, len1, str2, stIndex2, len2, output, curIndex + 1); output[curIndex] = str2[stIndex2]; PrintAllInterLeaving(str1, stIndex1, len1, str2, stIndex2 + 1, len2, output, curIndex + 1); } } void InputForInterLeaving() { char str1[]="AB"; char str2[]="CD"; char str3[] = "C"; char output[1000] = {0}; PrintAllInterLeaving(str1,0,strlen(str1),str2,0,strlen(str2),output,0); PrintAllInterLeaving(str1,0,strlen(str1),str3,0,strlen(str3),output,0); }