Lazao
Lazao

Reputation: 225

C Multiple arrays permutations algorithm

I'm trying to write a program that generates permutations upon a list of words stored into several arrays. For example, my program asks for 2 groups of words like this :

words #1: abc def ghi
words #2: 123 456

What i'm trying to have is this output:

abc 123 | abc 456 | def 123 | def 456 | ghi 123 | ghi 456

Or:

123 abc | 123 def | 123 ghi | 456 abc | 456 def | 456 ghi

The order doesn't matter.

I'd possibly make the group of words array with a non-fixed size. Then the input would be:

words #1: abc def ghi
words #2: 123 456
words #3: ** --

And the output:

abc 123 ** | abc 123 -- | abc 456 ** | abc 456 -- | def 123 ** | def 123 -- | def 456 ** | def 456 -- | ghi 123 ** | ghi 123 -- | ghi 456 ** | ghi 456 --

I think I had to consider using a recursive function but i'm a bit confused. Here is what I've written :

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

typedef struct permut_word_s {
    char *str;
} permut_word_t;

typedef struct permut_group_s {
    permut_word_t *words;
    int nb_words;
} permut_group_t;

static int split(char *str,
                 char *token,
                 permut_group_t *g) {
    permut_word_t *a = NULL;
    permut_word_t *t = NULL;
    char *p = NULL;
    int nbw = 0;
    int l = 0;

    if(!str || !token || !g) {
        return -1;
    }

    p = strtok(str, token);

    while(p != NULL) {
        if(!(t = realloc(a, (nbw + 1) * sizeof(permut_word_t)))) {
            return -1;
        }
        if(!(t[nbw].str = malloc(strlen(p) + 1))) {
            return -1;
        }
        memset(t[nbw].str, 0, strlen(p) + 1);
        if(!(strncpy(t[nbw].str, p, strlen(p)))) {
            return -1;
        }
        nbw++;
        p = strtok(NULL, token);
        a = t;
    }

    g->words = a;
    g->nb_words = nbw;

    return 0;
}

void word_free(permut_word_t *w) {
    if(!w) {
        return;
    }

    if(w->str) {
        free(w->str);
    }

    return;
}

void group_free(permut_group_t *g) {
    int i = 0;

    if(!g) {
        return;
    }

    for(; i < g->nb_words; i++) {
        if(&g->words[i]) {
            word_free(&g->words[i]);
        }
    }

    free(g->words);
    return;
}

void permut(permut_group_t *g,
            int cur,
            int len) {
    int i = 0;
    int j = 0;

    if(cur == len) {
        return;
    }

    for(i = cur; i < len; i++) {
        for(j = 0; j < g[cur].nb_words; j++) {
            printf("%s ", g[cur].words[j].str);
        }
        permut(g, cur + 1, len);
    }
}

int main(int argc, char **argv) {
    char buf[1024] = { 0 };
    permut_group_t *groups = NULL;
    int len = 0;

    (void)argc;
    (void)argv;

    if(!(groups = malloc(2 * sizeof(permut_group_t)))) {
        return -1;
    }

    fprintf(stdout, "words #1: ");
    fgets(buf, 1024, stdin);
    split(buf, " ", &groups[0]);
    len++;

    fprintf(stdout, "words #2: ");
    fgets(buf, 1024, stdin);
    split(buf, " ", &groups[1]);
    len++;

    permut(&groups[0], 0, len);

    group_free(&groups[0]);
    group_free(&groups[1]);
    free(groups);

    return 0;
}

How can do it correctly knowing that the groups array may have a variable size?

Upvotes: 3

Views: 495

Answers (1)

Stephan Lechner
Stephan Lechner

Reputation: 35154

Try the following:

void permut(permut_group_t *g,
            int cur,
            int len, char *result=NULL) {
    int j = 0;

    if(cur == len) {
        printf("%s\n", result);
        return;
    }

    for(j = 0; j < g[cur].nb_words; j++) {
        char nextLine[200];
        if (cur==0)
            strncpy (nextLine, g[cur].words[j].str, 200);
        else
            snprintf (nextLine, 200, "%s;%s", result, g[cur].words[j].str);

        permut(g, cur + 1, len, nextLine);
    }
}

The following input

words #1: AB CD
words #2: 11 22 33

produces then the following output:

AB;11
AB;22
AB;33
CD;11
CD;22
CD;33

The trick is that when collecting the results, intermediately achieved combinations have to be kept and passed to the next level. Therefore, signature of function permut has been extended by an (optional) parameter char* result, which serves as "memory" for these intermediate (or final) combinations.

The signature of the permutation function is now permut(permut_group_t *g, int cur, int len, char *result=NULL); from function main, you can keep your call permut(&groups[0], 0, len) as is, because optional parameter result can be omitted when parameter cur is 0;

Note that I extended also function split in order to strip any trailing new lines, such that these new lines do not get copied into the results:

char *newLine = strrchr(str, '\n');
if (newLine)
    *newLine = '\0';

Upvotes: 1

Related Questions