Создание перестановок в списке строк с использованием только одного символа - PullRequest
1 голос
/ 25 мая 2020

Я пытаюсь сгенерировать перестановки, используя список строк, принимающих один символ один раз. Ниже приведен код ввода и вывода, который мне нужен. Можем ли мы просто делать это итеративно? Также я не нахожу точного метода.

String[] lst = new String[]{"abc", "def", "ghi"}; //Given
String[] permutations = new String[]{ //To Generate
            "adg", "adh", "adi",
            "aeg", "aeh", "aei",
            "afg", "afh", "afi",
            "bdg", "bdh", "bdi",
            "beg", "beh", "bei",
            "bfg", "bfh", "bfi",
            "cdg", "cdh", "cdi",
            "ceg", "ceh", "cei",
            "cfg", "cfh", "cfi",
 };

Обновление: я не ищу только приведенный выше пример с размером списка = 3. Он может быть любого размера, и каждая строка может иметь разную длину.

Например: list = [ "ab", "abc", "defghi", "x", "einsigl"]

Ответы [ 3 ]

1 голос
/ 25 мая 2020

Вот один из способов сделать это: должен работать с произвольным количеством слов произвольной длины (не включая 0).

   String[] lst = new String[] {
        "abc",
        "def",
        "ghi"
    };
    int numWords = lst.length;
    int wordlen = lst[0].length();
    int numPerms = (int) Math.pow(wordlen, numWords);
    char[][] perms = new char[numPerms][numWords];
    char[][] chararr = Arrays.stream(lst)
      .map(String::toCharArray)
      .toArray(i -> new char[i][wordlen]);
    for (int i = 0; i < numWords; i++) {
        double permsLocal = Math.pow(wordlen, i + 1);
        int numRepeats = (int) Math.ceil((numPerms / permsLocal));
        int repeats = (int)(permsLocal / wordlen);
        for (int x = 0; x < repeats; x++) {
            char[] word = chararr[i];
            for (int j = 0; j < wordlen; j++) {
                char c = word[j];
                for (int k = 0; k < numRepeats; k++) {
                    perms[(x * wordlen * numRepeats) + k + j * numRepeats][i] = c;
                }
            }
        }
    }
    String[] permutations = Arrays.stream(perms)
      .map(String::new)
      .toArray(String[]::new);

Вывод:

[adg, adh, adi, aeg, aeh, aei, afg, afh, afi, bdg, bdh, bdi, beg, beh,
bei, bfg, bfh, bfi, cdg, cdh, cdi, ceg, ceh, cei, cfg, cfh, cfi]

Ссылка на реплику: https://repl.it/repls/BoilingExcitingAttributes

1 голос
/ 26 мая 2020

В этом ответе я расскажу, как я решил эту проблему, чтобы найти алгоритм, который работает для массива любой длины для слов, которые могут быть любой длины и не обязательно должны быть одинаковой длины.

Сначала я сделаю рекурсивное решение, а затем преобразовываю его в итеративное.

Самый простой способ ответить на подобные проблемы - подумать о них рекурсивно:

Генерация всех перестановок [] должна вернуть [""] Генерация всех перестановок непустого списка означает, для каждой буквы c в первом слове в списке вернуть все перестановки в остальной части списка с добавлением c впереди.

Это можно записать в Java следующим образом:

public static List<String> generatePermutationsRecursiveSlow(String[] words) {
    if (words.length == 0)
        // base case
        return Collections.singletonList("");
    else {
        // recursive case

        // result list
        ArrayList<String> permutations = new ArrayList<>();

        // split array into item 0 and items [1..end] 
        String firstWord = words[0];
        String[] otherWords = new String[words.length - 1];
        System.arraycopy(words, 1, otherWords, 0, words.length - 1);

        // recurse to find permutations for items [1..end]
        List<String> otherWordsPermutations = generatePermutationsRecursiveSlow(otherWords);

        // for each character in the first word 
        for (char c : firstWord.toCharArray()) {
            // for each permutation from the recursive call's results
            for (String otherWordsPermutation : otherWordsPermutations) {
                // prepend this character onto the permutation and add it to the results
                permutations.add(c + otherWordsPermutation);
            }
        }
        return permutations;
    }
}
  • Вызов generatePermutationsRecursiveSlow(new String[0]) возвращает [""].
  • Вызов generatePermutationsRecursiveSlow(new String[]{"cd"}) приведет к тому, что локальная переменная c будет равна 'c', и она будет рекурсивна с пустым массивом в качестве аргумента, что делает otherWordsPermutations равным [""], поэтому он добавит 'c' + "" (что составляет "c") к результатам, затем он сделает то же самое для 'd', добавив "d" к результатам.
  • Вызов generatePermutationsRecursiveSlow(new String[]{"ab", "cd"}) будет означать, что когда c равно 'a', он добавит в список результатов 'a'+"c", затем 'a'+"d", and whencis 'b ', it will add' b '+ "c" and' b '+ "d" `

Аналогичная, но лучше оптимизированная версия, которая работает в таким же образом можно записать так:

public static List<String> generatePermutationsRecursive(String[] words) {
    ArrayList<String> permutations = new ArrayList<>();
    int wordLen = words.length;

    generatePermutationsRecursive(words, permutations, new char[wordLen], 0);

    return permutations;
}

public static void generatePermutationsRecursive(String[] words, ArrayList<String> permutations, char[] word, int i) {
    if (i == word.length) {
        // base case
        permutations.add(new String(word));
    } else {
        for (int j = 0; j < words[i].length(); j++) {
            // equivalent of prepending
            word[i] = words[i].charAt(j);
            // recurse
            generatePermutationsRecursive(words, permutations, word, i + 1);
        }
    }
}

Это лучше оптимизировать, поскольку он использует параметр word, чтобы избежать добавления O(n) к строке, вместо этого изменяя массив символов. Он также вводит параметр i, который является эффективным начальным индексом массива, что позволяет избежать копирования частей входного массива.

Это можно преобразовать в итерационный подход, отслеживая переменные, которые изменяются между различными рекурсивными вызовами с использованием стека (вместо стека вызовов):

private static List<String> generatePermutationsIterative(String[] words) {
    // in the recursive version, each recursive function call would have its own local copy of `i` and `j`
    // simulate that here with 2 stacks
    ArrayDeque<Integer> i_stack = new ArrayDeque<>(words.length);
    ArrayDeque<Integer> j_stack = new ArrayDeque<>(words.length);

    i_stack.add(0);
    j_stack.add(0);

    char[] word = new char[words.length];
    ArrayList<String> permutations = new ArrayList<>();

    while (!i_stack.isEmpty()) {
        int i = i_stack.removeLast();
        int j = j_stack.removeLast();

        if (i == words.length) {
            // base case
            permutations.add(new String(word));
            continue;
        }

        if (!(j < words[i].length())) {
            // reached end of loop `for (int j = 0; j < words[i].length(); j++)`
            continue;
        }

        // if not reached end of loop `for (int j = 0; j < words[i].length(); j++)` yet,
        // then increment `j` and allow next iteration to happen
        i_stack.add(i);
        j_stack.add(j + 1);

        word[i] = words[i].charAt(j);

        // recurse
        i_stack.add(i + 1);
        j_stack.add(0);
    }

    return permutations;
}

Код здесь

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

0 голосов
/ 25 мая 2020

Это можно сделать следующим образом:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        String[] lst = new String[] { "abc", "def", "ghi" };
        List<String> list = new ArrayList<>();
        for (char a : lst[0].toCharArray()) {
            for (char b : lst[1].toCharArray()) {
                for (char c : lst[2].toCharArray()) {
                    list.add(new String(new char[] { a, b, c }));
                }
            }
        }

        // Convert to array
        String[] permutations = list.toArray(new String[0]);

        // Display
        System.out.println(Arrays.toString(permutations));
    }
}

Вывод:

[adg, adh, adi, aeg, aeh, aei, afg, afh, afi, bdg, bdh, bdi, beg, beh, bei, bfg, bfh, bfi, cdg, cdh, cdi, ceg, ceh, cei, cfg, cfh, cfi]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...