查找给定字符串排列的优化算法?

时间:2013-12-12 13:17:02

标签: java algorithm optimization permutation

在下面的代码中,我找到了给定输入字符串的所有可能的排列,并将它们存储在列表中,然后计算它们之间的回文。这在输入字符串长度小于10时工作正常。但是当输入字符串时如果长度大于10,则需要花费大量时间来查找排列。我想知道在这里可以优化什么来获得恒定的执行时间?

private static char[] inputArray;
private static List<String> listOfpermutations = new ArrayList<>();
private static int count;

public static void main(String s[]) throws IOException {
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    String input = reader.readLine();
    inputArray = input.toCharArray();
    permutation(0);
    for (String combination : listOfpermutations) {
        if (combination.equals(new StringBuilder(combination).reverse().toString())) {
            count++;
        }
    }
    System.out.println(count);
}

public static void permutation(int start) 
{
    String temp = "";
    if (start != 0) {
        if (start == inputArray.length) {
            for (int i = 0; i < start; i++) {
                temp = temp + inputArray[i];
            }
            if (!listOfpermutations.contains(temp)) {
                listOfpermutations.add(temp);
            }
        }

    }
    for (int i = start; i < inputArray.length; i++) 
    {
        swap(start, i);
        permutation(start + 1);
        swap(start, i);
    }
}

static void swap(int pos1, int pos2) {
    char temp = inputArray[pos1];
    inputArray[pos1] = inputArray[pos2];
    inputArray[pos2] = temp;
}

测试输入:

  1. aaabbb //很棒
  2. ccccddddcc //工作正常
  3. ccccddddcce //这里花了太长时间

4 个答案:

答案 0 :(得分:4)

IMO这是一个数学问题,而不是算法问题。

由于您只对回文字符串的数量感兴趣,因此无需生成所有可能的排列。

计算字符串中每种类型的字符数。将字符除以2。计算该半串的排列数。这将是答案,因为字符串的另一半只是反映了这一半。

例如,如果字符串是aabbcc,则计数a = 2,计数b = 2,计数c = 2.

因此,我们将这些减半以形成abc,以6种方式对其进行置换。这将是回文的排列数。

(您需要检查字符串中的字符数是奇数还是偶数)

答案 1 :(得分:1)

你可以在运行时间方面得到显着改善,你不会产生所有的n!排列。

由于您正在寻找回文,因此您的输入数据应包含许多重复字符。您生成排列的方式将生成许多相同的排列。 (作为副作用,你会多次计算某些排列)。

相反,请在lexicographic order

中生成排列

PS。此外,您可以跳过创建完整列表,但只需在完成下一个排列后立即检查回文。

PPS。事实上,Abhishek Bansal的想法相当不错。

计算字符串中每个字符的出现次数。如果可以回文, 然后所有的角色必须有偶数,除了可能只有一个。

将每个计数除以2并在除法后按字母顺序创建一个带有该计数的字符串。例如,从“abcccabaa”获取字符串“aabc”(请注意c具有奇数,并且在新字符串中出现一次)。

从结果字符串中,生成并计算字典顺序中的所有排列。 这将是你的答案。您不需要检查回文,因为您可以通过这种方式生成所有可能的回文。每个这样的排列将代表回文的一半。整个回文将是上半部分,可能后面跟着一个具有奇数的字符的单个实例,其次是反向的上半部分。例如,前几个回文将是

"aabc" + "c" + "cbaa"
"aacb" + "c" + "bcaa"
"abac" + "c" + "caba"

答案 2 :(得分:0)

一些性能提示:

  • 确定列表的大小,并将该大小作为构造函数的一部分传递。你花了很多时间来增加名单。
  • 根本不要使用列表。在构造排列时,只需确定排列是否是回文。
  • 尝试创建独特的排列,然后根据排列创建回文(例如“abac”会给你“abacaba”)。这应该给你与现在相同的设置。

答案 3 :(得分:0)

有一种更好的方法,可以在很大程度上减少您的时间复杂度。

实施步骤: -

  1. 首先将字符串缩减为压缩形式。 例如,aaabbb减少到a3b3。 ccccddddcc缩减为c6d4。
  2. 接下来,如果字符串的长度是偶数,则每个字符必须具有偶数(频率)。如果每个角色都没有偶数,那么它的回文排列为零。 并且,如果字符串的长度是奇数,则它们必须恰好是具有奇数(频率)的一个字符。如果最多一个元素没有奇数,则它具有零回文置换。 例如,aaabbb
    字符串长度 - 6(偶数)
    但是'a'和'b'字符有奇数 因此,它的回文排列为零。

    e.g。 ccccddddcc
    字符串长度 - 10(偶数)
    所有字符'c'和'd'都有统计数字 因此,它具有回文排列。

    e.g。 ccccd
    字符串长度 - 5(奇数)
    'c'的计数为4(偶数),'d'的计数为奇数,即1 因此,它具有回文排列。

  3. 现在,在第二点你必须知道字符串是否有回文排列,现在我们只需要找出有多少排列。

  4. 当String缩小为c6d4之类的格式并且你知道它存在排列时,你可以像这样继续:

    例如c4d2 take可以减少到3个字符(因为2c和1d,因为在回文中其余字符串是相同的)它们如何排列, 所以答案是3!/(2!* 1!)= 3

    例如c4e4d1可以减少到4个字符(因为2c和2e,因为在回文中,其余字符串是相同的,1d字符在它们之间),所以答案是4!/(2!* 2!)= 6.