Возможные струнные перестановки смеси мультимножества и множества - PullRequest
4 голосов
/ 19 апреля 2019

Я пытаюсь получить все возможные комбинации char*.Эта строка состоит из четырех значений: двух цифр и двух разных букв.Например:

char *text = "01ab";

Должно быть

image

so

image

different combinations for my example string, which seems to be true (done by hand):

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

My approach would be the same as the one I did by hand: get all combinations with the first index of text at the start, then all combinations of the second index of text and so on. So something like this:

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

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

    for(int i=0 ; i

Now I am a bit confused about how to continue after the first index of the combination. There are несколько примеров для всех комбинаций, но моя проблема, кажется, немного отличается, так как мои числа в комбинации может быть одним и тем же , и только буквы должны быть разными .

Как мне добиться печати всех комбинаций на моей консоли?Я реализовал функцию, которая вычисляет количество возможных комбинаций, поэтому предположим, что это уже сделано.

Было бы неплохо, если бы алгоритм работал для любых значений numbers и letters, например,все комбинации текста lenght 6 с four different numbers и двумя different letters также могут быть рассчитаны.

Язык не имеет значения, любой совет приветствуется.

Ответы [ 4 ]

3 голосов
/ 19 апреля 2019

Ваша проблема может быть решена с помощью стратегии возврата.Это создаст все возможные комбинации.

Я знаю, что вы хотите удалить повторяющиеся комбинации, если два числа совпадают, чтобы избавиться от них, вы можете использовать хеш-таблицу для хранения сгенерированной комбинации, а затем каждый раз, когда вы генерируете новую комбинациюперенесите его в хеш-таблицу, чтобы проверить, был ли он сгенерирован или нет (если нет, введите его в хеш-таблицу и распечатайте, игнорируйте печать наоборот).Там для моего псевдокода, как следует (вы можете иметь лучший способ):

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 голос
/ 19 апреля 2019

Я использовал 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);
1 голос
/ 19 апреля 2019

Рекурсивный подход был бы простым способом здесь.
Давайте рассмотрим, что вы хотите сгенерировать все строки с m буквами, все они разные, взятые из массива letters[m] и чисел n,это может быть повторено, взято из массива numbers[N] (n может быть меньше, такого же размера больше, чем N, это не имеет значения).
Вы можете решить это таким образом (псевдокод, Стиль C):

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, тогда я могу добавить любую букву, которая еще не была добавлена ​​(массив логических значений помогает убедиться, что это так),и я могу продолжить.Если моя строка готова к печати, выведите ее на печать.

Первый вызов должен быть выполнен с использованием логического массива, инициализированного везде: false и 0 для значений nb_letters_in_result и nb_numbers_in_result,поскольку вы еще не добавили ни одной цифры или буквы в строку результата.
Что касается строки результата, поскольку вы кодируете в C, не забудьте выделить для нее память:

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

иобнулить его:

current_string[m+n] = '\0';
0 голосов
/ 20 апреля 2019

Я также нашел интересное решение для моего вопроса.Предположим, мой пример строки 01ab.

Сначала мы хотим создать все комбинации чисел 01 и перестановки ab.Есть много примеров того, как решить эту проблему.

Так что теперь у нас есть все комбинации 01 и ab.Я назову их комбинации производителей :

10   ab
01   ba
11
00

Теперь мы хотим объединить все числа со всеми буквами, но с правилом

Порядок чиселили буквы не должны быть зарезервированы для каждой комбинации

Так что, если мы объединим 10 с ab, мы получим:

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 элементам в общей сложности, как я рассчитал в своем вопросе.

...