Java 仅使用一个字符在字符串列表上生成置换

Java 仅使用一个字符在字符串列表上生成置换,java,Java,我正在尝试使用一次使用一个字符的字符串列表生成置换。 下面是我想要的输入和输出代码。 我们能简单地迭代吗?。我也没有找到确切的方法 String[] lst = new String[]{"abc", "def", "ghi"}; //Given String[] permutations = new String[]{ //To Generate "adg", "adh", "adi", "aeg", "aeh", "aei",

我正在尝试使用一次使用一个字符的字符串列表生成置换。 下面是我想要的输入和输出代码。 我们能简单地迭代吗?。我也没有找到确切的方法

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”]

您可以按如下方式操作:

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));
    }
}
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;
    }
}

这里有一种方法可以处理任意长度的任意数量的单词(不包括0)


链接到repl.it:

在这个答案中,我将介绍我是如何解决这个问题的,以找到一种算法,该算法适用于任意长度的数组,适用于任意长度的单词,并且不要求所有单词的长度都相同

我将首先生成一个递归解,然后将其转换为一个迭代解

回答此类问题的最简单方法是递归地思考:

生成
[]
的所有排列应返回
[“”]
生成非空列表的所有排列意味着,对于列表中第一个单词中的每个字母
c
,返回列表其余部分前面有
c
前缀的所有排列

这可以用Java编写,如下所示:

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));
    }
}
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;
    }
}
这是更好的优化,因为它使用
word
参数来避免
O(n)
通过修改字符数组来预加字符串。它还引入了参数
i
,它是数组的有效开始索引,可以避免复制输入数组的部分

通过使用堆栈(代替调用堆栈)跟踪不同递归调用之间发生变化的变量,可以将其转换为迭代方法:

private static List generatePermutationsIterative(字符串[]字){
//在递归版本中,每个递归函数调用都有自己的本地副本'i'和'j'`
//在这里用两个堆栈来模拟它
ArrayDeque i_stack=新的ArrayDeque(words.length);
ArrayDeque j_stack=新的ArrayDeque(words.length);
i_stack.add(0);
j_叠加(0);
char[]word=新字符[words.length];
ArrayList置换=新的ArrayList();
而(!i_stack.isEmpty()){
int i=i_stack.removeLast();
int j=j_stack.removeLast();
if(i==单词长度){
//基本情况
添加(新字符串(单词));
继续;
}
if(!(j
代码


作为旁注,看看Haskell对这个问题的两行解决方案有多酷(承认它不是迭代的,但它应该有尾部调用优化,使其与迭代解决方案一样快)。

只有当你知道你有多少单词时,这不才有效吗?即使你知道这一点,维护它仍然是一项艰巨的工作。我认为你在最后一篇专栏文章中有一个错误(aei、bei和cei翻了一倍)。谢谢你指出,我已经修复了它。
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;
    }
}
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);
        }
    }
}
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;
}