多集和集合的混合的可能的字符串排列

时间:2019-04-19 16:02:52

标签: c arrays algorithm combinations c-strings

我正在尝试获取char*的所有可能组合。此字符串包含四个值:两个数字和两个不同字母。例如:

char *text = "01ab";

应该是

如此

示例字符串的不同组合,这似乎是正确的(手工完成):

Combinations for values: 0, 1, a, b:

0 0 a b     1 1 a b     a 0 0 b     b 0 0 a
0 0 b a     1 1 b a     a 0 1 b     b 0 1 a
0 1 a b     1 0 a b     a 0 b 0     b 0 a 0
0 1 b a     1 0 b a     a 0 b 1     b 0 a 1
0 a 0 b     1 a 1 b     a 1 b 0     b 1 0 a
0 a 1 b     1 a 0 b     a 1 b 1     b 1 1 a
0 a b 0     1 a b 1     a 1 0 b     b 1 a 0
0 a b 1     1 a b 0     a 1 1 b     b 1 a 1
0 b 0 a     1 b 1 a     a b 0 0     b a 0 0
0 b 1 a     1 b 0 a     a b 0 1     b a 0 1
0 b a 0     1 b a 1     a b 1 0     b a 1 0
0 b a 1     1 b a 0     a b 0 0     b a 1 1

我的方法与我手工进行的方法相同: 首先获取text第一索引的所有组合,然后获取text第二索引的所有组合,依此类推。像这样:

void printPasswordCombinations()
{
    char *all_values = "01ab";
    int len = strlen(all_values);

    char *tmp_pwd = malloc(sizeof(len) * sizeof(char));

    for(int i=0 ; i<len ; i++)
    {
        tmp_pwd[0] = all_values[i];

        /* len-1, since the first index is already set. */
        for(int j=0 ; j<len-1 ; j++)
        {

        }
    }

    printf("%s\n", tmp_pwd);
    free(tmp_pwd);
}

现在,我对如何在组合的第一个索引之后继续操作感到有些困惑。所有组合都有several examples,但是我的问题似乎有些不同,因为组合中的数字可以相同,并且只有字母必须不同

如何实现将所有组合打印到控制台? 我实现了一个计算可能组合数量的函数,因此只需假设已经完成。

如果该算法适用于任意数量的numbersletters,那就太好了,例如lenght 6four different numbers的文本和两个的所有组合different letters也可以计算出来。

语言无关紧要,任何建议都值得赞赏。

4 个答案:

答案 0 :(得分:3)

您的问题可以通过回溯策略来解决。它将创建所有 可能的组合。

我知道您想在两个数字相同的情况下删除重复的组合,要摆脱它们,您可以使用哈希表存储生成的组合,然后每次生成新组合时都将其携带到哈希表以检查它是否已生成(如果未生成,则将其输入到哈希表中并打印出来,反之则忽略打印)。如下是我的伪代码(您可以有一个更好的方法):

val characters = [/*4 characters*/]
val picked = [false,false,false,false]
val hashtable = empty

function genetate(string aCombin):
    if aCombin.size == 4:
         if(hashtable.contains(aCombin)):
               //do nothing
         else:
               print(aCombin)
               hashtable.add(aCombin)
    for i in characters.size:
         if(picked[i]==false):
             picked[i]=true
             aCombin.add(characters[i])
             generate(aCombin)
             picked[i]=false //backtrack
             aCombine.popBack() //remove the last character

答案 1 :(得分:1)

在这里,递归方法将是简单的方法。
让我们考虑一下,您想生成包含m个字母的所有字符串,它们都是不同的,取自letters[m]数组,以及n个数字,这些数字可以重复,取自{{ 1}}数组(numbers[N]可以更小,大小也可以大于n,这没关系)。
然后,您可以通过这种方式解决此问题(伪代码,C风格):

N

要解决此类问题,必须采用递归方法。其工作原理如下:
如果我已经有一个包含void print_them_all(char *numbers, int nb_numbers_in_result, int n \ char *letters, bool *is_letter_used, int nb_letters_in_result, int m, char *current_string){ if ((nb_numbers_in_result == n) && (nb_letters_in_result == m)){ // terminal case -> time to print the current string printf("%s\n", current_string); } else { // string not completely built yet // get the index where the next char will be added current_index = nb_letters_in_result + nb_numbers_in_result; if (nb_numbers_in_result < n){ // still possible to add a number for (int i = 0; i < N; i++){ current_string[current_index] = numbers[i]; print_them_all(numbers, nb_numbers_in_result+1, n, \ letters, is_letter_used, nb_letters_in_result, m, \ current_string); } } if (nb_letters_in_result < m){ // still possible to add a letter for (int i = 0; i < m; i++) { if (is_letter_used[i] == false){ // check the letter has not been added yet // keep track that the letter has been added by 'marking' it is_letter_used[i] = true; // add it current_string[i] = letters[i]; // recursive call print_them_all(numbers, nb_numbers_in_result, n, \ letters, is_letter_used, nb_letters_in_result+1, m, \ current_string); // now 'unmark' the letter is_letter_used[i] = false; } } } } } 个数字的字符串,k,那么我可以在其中添加任何数字,然后继续操作(现在我的字符串中将包含k<n个数字) 。
如果我已经有一个包含k+1个字母的字符串,k,那么我可以添加尚未添加的任何字母(布尔数组有助于确保是这种情况),可以继续。 如果我的字符串可以打印了,请打印。

第一次调用应该使用在各处都初始化为k<m的布尔数组,对于false0的值为nb_letters_in_result的情况,因为您尚未添加任何结果字符串中的数字或字母。
至于结果字符串,由于您使用C语言编写代码,因此请不要忘记为其分配内存:

nb_numbers_in_result

,然后将其空终止:

char *current_string = malloc((m+n+1) * sizeof(char));

答案 2 :(得分:1)

我之所以使用Javascript,是因为Javascript可以在浏览器中运行并且语言无关紧要。下面的方法使用递归。尝试使用“ 0123ab”。

'use strict';

const input = '01ab';

const reLetters = /[^0-9]/g;
const reDigits = /[0-9]/g;
const nLetters = input.replace(reDigits, '').length;
const nDigits = input.replace(reLetters, '').length;

const findComb = cur => {
    if (cur.length === input.length)
        return console.log(cur);
    for (let l of input) {
        if (l.match(reDigits)) {
            if (cur.replace(reLetters, '').length === nDigits) continue;
        } else {
            if (cur.match(l) || cur.replace(reDigits, '').length === nLetters) continue;
        }
        findComb(cur + l);
    }
}

findComb('');

这里是不带“删除字母以计数数字”的版本。它的效率提高了约20%。我使用nodejs和'01234abc'作为输入进行测量。

'use strict';

const input = '01ab';

const reLetters = /[^0-9]/g;
const reDigits = /[0-9]/g;
const maxLetters = input.replace(reDigits, '').length;
const maxDigits = input.replace(reLetters, '').length;

const findComb = (cur = '', nDigits = 0, nLetters = 0) => {
    if (cur.length === input.length)
        return console.log(cur);
    for (let l of input) {
        if (l.match(reDigits)) {
            if (nDigits < maxDigits)
                findComb(cur + l, nDigits + 1, nLetters);
        } else {
            if (cur.match(l)) continue;
            if (nLetters < maxLetters)
                findComb(cur + l, nDigits, nLetters + 1);
        }
    }
}

findComb();

这里没有递归。这是最慢的,但可以改善。

'use strict';

const input = '01ab';

const reLetters = /[^0-9]/g;
const reDigits = /[0-9]/g;
const nLetters = input.replace(reDigits, '').length;
const nDigits = input.replace(reLetters, '').length;

let cur = '', l = undefined;
do {
    l = input[input.indexOf(l) + 1];
    if (l !== undefined) {
        if (l.match(reDigits)) {
            if (cur.replace(reLetters, '').length === nDigits) continue;
        } else {
            if (cur.match(l) || 
                cur.replace(reDigits, '').length === nLetters) continue;
        }
        if (cur.length + 1 === input.length) {
            console.log(cur + l);
        } else {
            cur = cur + l;
            l = undefined;
        }
    } else {
        l = cur[cur.length - 1];
        cur = cur.slice(0, -1);
    }
} while (cur != '' || l != undefined);

答案 3 :(得分:0)

我也找到了一个有趣的解决方案。 假设我的示例字符串01ab

首先,我们要创建数字01ab的排列的所有组合。 有很多解决此问题的示例。

因此,现在我们拥有01ab的所有组合。我会称它们为生产者组合

10   ab
01   ba
11
00

现在,我们希望将所有数字与所有字母组合在一起,但要符合规则

不能为每个组合保留数字或字母的顺序

因此,如果我们将10ab结合使用,则会得到:

10ab
1a0b
a10b

现在,我们将b移到左侧,直到它即将与a交换位置,由于我的规则,这是被禁止的。我们对每种组合都这样做:

10ab produces:

10ab

因为b已经在a旁边了。

1a0b produces:

1ab0

所以我们又有了一个组合。

a10b produces:

a1b0
ab10

所以我们还有2种组合。

现在我们拥有01 and ab的所有可能组合:

10ab
1a0b
a10b
1ab0
a1b0
ab10

由于我们的生产者组合包含8个元素,因此我们必须对所有元素进行8次此步骤。产生的组合将始终包含6个元素,例如在我的示例中,这导致我在问题中计算出的总数为48个元素。