【问题标题】:Can't produce all permutations of a String (iteratively)不能产生字符串的所有排列(迭代地)
【发布时间】:2016-05-04 15:48:30
【问题描述】:

所以我正在做一些 Java 练习,最近引起我注意的一个是尝试使用迭代生成 String 的所有排列。网上有很多例子 - 但是,其中很多看起来非常复杂,我无法理解。

我尝试过使用我自己的方法,当使用长度为 3 的字符串进行测试时,它可以正常工作。方法是(对于每个字母)沿着字符串继续移动一个字母,将它与它前面的任何字母交换。例如。

index:  012
string: abc 

(iteration 1) swap 'a' (index 0) with letter after it 'b' (index 0+1) : bac
(iteration 2) swap 'a' (index 1) with letter after it 'c' (index 1+1) : bca
(iteration 3) swap 'a' (index 2) with letter after it 'b' (index 0)   : acb
current permutations: abc (original), bac, bca, acb

(iteration 3) swap 'b' (index 1) with letter after it 'c' (index 1+1) : acb
(iteration 4) swap 'b' (index 2) with letter after it 'a' (index 0)   : bca
(iteration 5) swap 'b' (index 0) with letter after it 'c' (index 1)   : cba
current permutations: abc (original), bac, bca, acb, acb, cba

...

这就是我在 Java 中实现它的方式:

String str = "abc"; // string to permute
char[] letters = str.toCharArray(); // split string into char array
int setLength = factorial(letters.length); // amount of permutations = n!
HashSet<String> permutations = new HashSet<String>(); // store permutations in Set to avoid duplicates
permutations.add(str); // add original string to set

// algorithm as described above
for (int i = 0; i < setLength; i++) {
    for (int j = 0; j < letters.length; j++) {
        int k;
        if (j == letters.length - 1) {
            k = 0;
        } else {
            k = j + 1;
        }
        letters = swap(letters, j, k);
        String perm = new String(letters);
        permutations.add(perm);
    }
} 

问题是,如果我输入一个长度为 4 的字符串,我只会得到 12 个排列 (4x3) - 如果我输入一个长度为 5 的字符串,我只会得到 20 个排列 (5x4)。

我可以对该算法进行简单的修改以获得所有可能的排列吗?还是这种特殊方法仅适用于长度为 3 的字符串?

感谢任何反馈!

【问题讨论】:

  • 我不确定是否有一种简单的方法可以用你的方法来做到这一点,但你更有可能在 3 号字符串上走运。我建议解决这个问题的是创建某种树,一个包含所有可能性的树
  • 迭代往往是 Java 中的最佳解决方案,但是当您需要的循环数量与问题成正比时,递归很可能是最佳解决方案。虽然理论上,您可以使用自己的堆栈将任何递归方法转换为循环,但这通常非常复杂,至少我建议您在尝试之前递归地实现它。
  • 您能否提供您的整个 Java 代码以及其他两个函数,这样更易​​于执行。

标签: java string algorithm iteration permutation


【解决方案1】:

虽然我不知道有什么方法可以扩展您当前的切换位置方法(我之前尝试过这种方法,但没有成功),但我确实知道一种相当简单的方法

//simple method to set up permutate
private static void permutations(String s)
{
  permutate(s, "");
}

//takes the string of chars to swap around (s) and the base of the string to add to
private static void permutate(String s, String base)
{
  //nothing left to swap, just print out
  if(s.length() <= 1)
    System.out.println(base + s);
  else
    //loop through the string of chars to flip around
    for(int i = 0; i < s.length(); i++)
      //call with a smaller string of chars to flip (not including selected char), add selected char to base
      permutate(s.substring(0, i) + s.substring(i + 1), base + s.charAt(i));
}

此递归的目标是将尽可能多的处理委托给其他事情,一点一点地分解问题。很容易解决这个问题,首先选择一个字符,然后告诉一个函数找出其余的。然后可以对每个字符执行此操作,直到它们都被选中一次

【讨论】:

    【解决方案2】:

    假设输入是“abcd”。这就是你的算法的工作方式

    背影

    背影

    bcad

    bcda

    如果您仔细观察,“a”被定位在所有索引处,并且随后的连续字母被“a”替换。但是,在您的算法产生“bacd”之后 - 它后面也应该跟着“badc”,这将在您的输出中丢失。

    对于长度为 4 的字符串,当您将排列数计算为阶乘时,您了解第一个位置可以被 4 个字符占据,然后是 3、2 和 1。但是,在您的情况下,当前两个位置被“ba”占据第三个位置有两种可能,即c和d。虽然您的算法正确找到“cd”,但它无法找到“dc” - 因为循环不会将问题分解为进一步的子问题,即“cd”有两个排列,分别是“cd”和“dc”。

    因此,你的排列数和实际答案的差异会随着字符串长度的增加而增加。

    为了轻松地将问题分解为子问题并解决它,许多算法都使用递归。

    但是,您可以查看 Generate list of all possible permutations of a string 以获得良好的迭代答案。

    另外,随着字符串长度的增长,不建议计算排列数。

    【讨论】:

    • 新答案,觉得很有用
    猜你喜欢
    • 1970-01-01
    • 2019-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-30
    • 2011-05-13
    相关资源
    最近更新 更多