如何在不相邻的相等元素的情况下随机排列字符串

时间:2019-05-29 08:17:36

标签: java algorithm random permutation

因此,我有一个示例字符串,例如“ aaabbbc”,我想随机对其进行随机排序,但是结果中不应有两个连续的字母相同。输出应为“ abababc”或“ cbababa”或“ abacbab”等。

我已经尝试过使用PriorityQueue进行编码,它可以解决问题,但是只有一种方法不会随机生成许多方法来随机无序地打乱我的字符串。下面是我使用的代码。

    int n = str.length();
    int[] count = new int[MAX_CHAR];

    for (int i = 0; i < n; i++) {
        count[str.charAt(i) - 'a']++;
    }

    PriorityQueue<Key> pq = new PriorityQueue<>(new KeyComparator());
    for (char c = 'a'; c <= 'z'; c++) {
        int val = c - 'a';
        if (count[val] > 0) {
            pq.add(new Key(count[val], c));
        }
    }
    str = "";

    Key prev = new Key(-1, '#');
    while (pq.size() != 0) {
        Key k = pq.peek();
        pq.poll();
        str = str + k.ch;

        if (prev.freq > 0) {
            pq.add(prev);
        }

        (k.freq)--;
        prev = k;
    }

    if (n != str.length()) {
        return "";
    } else {
        return str;
    }

在尝试通过该算法随机生成时,我陷入了困境。我想要的结果是如上所述的动态输出。谢谢

1 个答案:

答案 0 :(得分:0)

假设您想要有效排列的平均分配: 如果您不关心内存或运行时的复杂性,则可以生成字符串(Generating all permutations of a given string)的所有排列,然后删除所有具有相同字母的字符串,最后从该列表中随机选择一个。

如果您确实想优化内存或运行时,请参阅链接的类似问题。

一些其他方法,如果您不需要相等的分布,但是仍然有机会找到任何有效的排列:

  • 您可以通过从其余字母中随机选择(与您的操作类似)来构建字符串,并在遇到死胡同时(没有有效的字母可供选择)回溯。参见Python permutation using backtracking
  • 您可以创建一个字符串,在其中从输入中选择随机字母而无需关心相邻的重复,然后迭代交换(随机选择)此随机起始点(请参阅Heap的算法),直到结果符合您的条件(或直到您返回)为止。一开始)。

回溯解决方案

在这里,我选择一个随机的nextChar,然后尝试构建一个不以该char开头的随机String。如果失败,请尝试另一个。通过递归,这最终将以随机顺序尝试所有组合,直到找到有效的组合为止。

private static final Random RANDOM = new Random();

public static void main(String[] args) {
    System.out.println(randomPerm("aaabcc"));
}

public static String randomPerm(String str) {
    Map<Character, Long> counts = str
            .chars()
            .mapToObj(c -> (char) c)
            .collect(groupingBy(Function.identity(), Collectors.counting()));
    return restPerm(null, counts);
}

public static String restPerm(Character previous, Map<Character, Long> leftover) {
    List<Character> leftKeys = new ArrayList<>(leftover.keySet());
    while (!leftKeys.isEmpty()) {
        Character nextChar = leftKeys.get(RANDOM.nextInt(leftKeys.size()));
        leftKeys.remove(nextChar);
        if (nextChar.equals(previous) || leftover.get(nextChar) == 0) {
            continue; // invalid next char
        }
        // modify map in place, reduce count by one
        Long count = leftover.compute(nextChar, (ch, co) -> co - 1);
        if (leftover.values().stream().noneMatch(c -> c > 0)) {
            return nextChar.toString();  // last char to use
        }
        String rest = restPerm(nextChar, leftover); // recurse
        if (rest != null) {
            return nextChar + rest; // solution found
        }
        leftover.put(nextChar, count + 1);
    }
    return null;
}

我认为这种解决方案更有可能找到在结果开头使用稀有字符的结果,因此分布将不相等。但是它使用的内存有限,可能会在计算所有排列之前完成,并且与您的方法有些相似。