1

My assignment is that code takes a string as input, and if there are x's in the string replace them with either a 0 or a 1, and print out all the possible string combinations. We have to use recursion for this as well. For example, if the input string was "1x0X" the output would be:

1000
1001
1100
1101

I'm really struggling with how I'm supposed to find all the permutations of the string without having the complete string yet. I have a series of function that combine to print out all permutations of a list of numbers, but I don't know how to make a function where it only permutes certain elements of a list.

Does anyone have any suggestions on how to accomplish this?

5
  • Read the whole string into memory. Pass it to your recursive function. That should make a copy of the string and find the position of the first x. Modify that position with a 0 and call the function recursively to do more substitutions as neededt. Then replace where the first x was with a 1 and call the function recursively to do more substitutions as needed. If you don't find any x's, print the string (base case for your recursion). Note that you need to be careful to make copies; if you destroy the x's in the input, you won't get a complete set of outputs. Commented Dec 5, 2017 at 1:38
  • 2
    And, in general, questions like this get snippy comments about "show us what you've tried", etc. Also, what are the constraints on the length of the strings you need to process? Commented Dec 5, 2017 at 1:39
  • @JonathanLeffler there are no constraints on the lengths of the strings. I'll try your suggestion, thanks. Commented Dec 5, 2017 at 1:42
  • I agree with @JonathanLeffler's suggestion except that you don't need to make copies of the string at each level of recursion. Especially in C it's easier to use a single copy of the string with it's embedded x's. The recursive function finds the next x, replaces it with 0, and calls itself. Upon return, it replaces the same 0 with 1 and calls itself again. Then it restores the x and returns. Commented Dec 5, 2017 at 3:55
  • 1
    @Gene: that also works as long as no-one gets careless and submits a string literal as the argument to the function. Commented Dec 5, 2017 at 4:08

3 Answers 3

2

Jonathan's initial suggestion

This code implements what I suggested in a comment essentially verbatim. It accepts either x or X as a valid marker because the examples in the question do too.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void map_x(const char *str)
{

    size_t xloc = strcspn(str, "xX");
    if (str[xloc] == '\0')
        printf("%s\n", str);
    else
    {
        char *copy = strdup(str);
        copy[xloc] = '0';
        map_x(copy);
        copy[xloc] = '1';
        map_x(copy);
        free(copy);
    } 

}

int main(void)
{
    char buffer[4096];
    while (fgets(buffer, sizeof(buffer), stdin) != 0)
    {
        buffer[strcspn(buffer, "\n")] = '\0';
        map_x(buffer);
    }
    return 0;
}

The main() function is essentially the same in all three variants. The use of strcspn() is a standard idiom that trims everything from the first newline onwards, or overwrites the end of the string if there is no newline in it.

Note that this solution is safe even if a read-only string literal is passed to the function; it does not modify the string that it is passed. The following solutions will both crash or otherwise fail if the initial string is in fact a read-only string literal.

It would be possible to determine the string length, allocate a VLA (variable length array) to take the string copy, and copy the string into the VLA. That would dramatically reduce the cost of allocating memory for the string (VLA allocation is much simpler than a general purpose memory allocator).

Gene's Suggestion

This code implements what Gene suggested in a comment. It will be more efficient because it does no extra memory allocation, an expensive operation on most systems.

#include <stdio.h>
#include <string.h>

static void map_x(char *str)
{

        size_t xloc = strcspn(str, "xX");
        if (str[xloc] == '\0')
            printf("%s\n", str);
        else
        {
            char letter = str[xloc];
            str[xloc] = '0';
            map_x(str);
            str[xloc] = '1';
            map_x(str);
            str[xloc] = letter;
        }

}

int main(void)
{
    char buffer[4096];
    while (fgets(buffer, sizeof(buffer), stdin) != 0)
    {
        buffer[strcspn(buffer, "\n")] = '\0';
        map_x(buffer);
    }
    return 0;
}

Mildly optimized variant

This optimizes the work by not rescanning the prefix that is already known to be free of x's.

/* SO 4764-4683 */
#include <stdio.h>
#include <string.h>

static void map_x(char *str, size_t offset)
{

        size_t xloc = strcspn(&str[offset], "xX") + offset;
        if (str[xloc] == '\0')
            printf("%s\n", str);
        else
        {
            char letter = str[xloc];
            str[xloc] = '0';
            map_x(str, xloc);
            str[xloc] = '1';
            map_x(str, xloc);
            str[xloc] = letter;
        }

}

int main(void)
{
    char buffer[4096];
    while (fgets(buffer, sizeof(buffer), stdin) != 0)
    {
        buffer[strcspn(buffer, "\n")] = '\0';
        map_x(buffer, 0);
    }
    return 0;
}

The difference in performance is probably not measurable on almost any input simply because the I/O time will dominate.

With all due respect to chux, I think that the code in the answer is more complex than necessary. The extra data structure seems like overkill.

Sign up to request clarification or add additional context in comments.

3 Comments

A big O assessment is interesting. The 2 major factors are string length L and number of X. A quick assessment suggests the "Gene's Suggestion" --> "Mildly optimized variant" reduces from O(X*L*2^X) to O(L*2^X).
Re: "answer is more complex than necessary. ". That answer avoided a fixed max buffer - like this one, to cope with OP's not so reasonable comment - there are no constraints on the lengths of the strings. IAC, the "no constraints on the lengths of the strings" is not reasonable as there is always limitations. IMO, a fixed max buffer is reasonable.
@chux: your Big-O analysis is interesting — thanks. If X gets to be remotely big, the 2^X term completely dominates and the factor of X is largely immaterial, or of minor consequence compared to 2^X. And for plausible test case sizes, the I/O time dominates; in fact, I can't think of circumstances where it wouldn't. Regarding 'no constraints on length', with all versions (including yours, I think) the constraints are primarily on reading the string. I used fgets() and a large enough buffer to not be a constraint for most tests. If that isn't big enough, there's POSIX getline().
2

Recursion is best used when the recursive depth is limited and not too big.

Yet setting aside that axiom, below is a double recursive solution. Its eats up stack space quickly (that is its biggest constraint) and so is not a robust solution. Yet it gets the job.

For "code takes a string as input,", use fgets() - not shown.

Yet in the spirit of recursion why not recurse the input too? The print_combo() recursion produces a linked list (LL) of characters and keeps track of the number of 'x' read. Once an end-of-line/end-of-file occurs, it is time to print and the linked-list starts with the last character.

The foo() recursion prints the LL in reverse order, passing in a binary mask to direct the x substitution of 0 or 1. The unsigned binary mask is good for typically 32 x's. That is another restriction.


If you must, mouse over for the code.

typedef struct node {
  const struct node *prev;
  int ch;
} node;

// Print the line
void foo(const node *prev, unsigned mask) {

    if (prev) {
        if (prev->ch == 'x' || prev->ch == 'X') {
          foo(prev->prev, mask >> 1);
          putchar("01"[mask & 1]);
        } else {
          foo(prev->prev, mask);
          putchar(prev->ch);
        }
      }
    }  

// Read, form the LL and then print
void print_combo(const node *prev, unsigned xcount) {

    node n = {.prev = prev, .ch = getchar()};
      if (n.ch == '\n' || n.ch == EOF) {
        for (unsigned mask = 0; mask < (1u << xcount); mask++) {
          foo(prev, mask);
          putchar('\n');
        }
      } else {
        print_combo(&n, xcount + (n.ch == 'x' || n.ch == 'X'));
      }
    }  

int main(void) {
  print_combo(NULL, 0);
}

Input

00x01x10x11

Output

00001010011
00001010111
00001110011
00001110111
00101010011
00101010111
00101110011
00101110111

Comments

1

I would do something a bit simpler. Just use a position parameter to iterate over the input string. Whenever you hit the 'x' character recurse twice, once for '0' and once for '1'. Make sure to reset the character back to 'x' after you return. Whenever you hit the digit character just recurse once. Increment the position parameter each time you recurse. When you hit the end of the string, print it out. With this idea, you'd get something like this:

#include <stdio.h>

void print_combo(char *str, int pos) {
    char c;

    c = str[pos];
    switch (c) {
            case '0':
            case '1':
                    print_combo(str, pos+1);
                    break;
            case 'x':
            case 'X':
                    str[pos] = '0';
                    print_combo(str, pos+1);
                    str[pos] = '1';
                    print_combo(str, pos+1);
                    str[pos] = c;
                    break;
            case '\0':
                    printf("%s\n", str);
                    break;
            default:
                    printf("bad input\n");
                    break;
    }
}


int main() {
    char str[10];
    strcpy(str, "1x0x");
    printf("printing %s\n", str);
    print_combo(str, 0);
    strcpy(str, "0x01x");
    printf("printing %s\n", str);
    print_combo(str, 0);
    strcpy(str, "0x01x0X1");
    printf("printing %s\n", str);
    print_combo(str, 0);
    return 0;
}

My output looks like this:

printing 1x0x
1000
1001
1100
1101
printing 0x01x
00010
00011
01010
01011
printing 0x01x0X1
00010001
00010011
00011001
00011011
01010001
01010011
01011001
01011011

4 Comments

note that because I change the contents of str inside print_combo you can't pass in a static string to start with, i.e. print_combo("010x1", 0); would probably cause a crash.
Your code works, but if you have a 200 character string with 4 x's in it, you will recurse through 200 levels, whereas the other answers will recurse through 4 levels. You could reduce the number of levels of recursion by not recursing (just iterating, increasing pos) until you come across an x, X or the end of string. The question says "strings" but only shows 0, 1, x, X; you could accept any characters rather than necessarily insisting on the limited alphabet — but I can see why you might do that. (If you make the x, X case into the default, you'd accept any input string.)
Oh, and I do believe I meant "if you make the 0, 1 case into the default", not the "x, X case".
@JonathanLeffler I need to double check that code, and see how to get rid of the extra recursive calls. I was mainly trying to omit the library calls that might confuse the OP, and just show the recursion.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.