在Java中使用递归的字符串排列

时间:2015-01-09 08:14:40

标签: java string recursion permutation anagram

我遇到了THIS帖子,其中非常努力地解释了打印所有字符串的递归解决方案。

public class Main {
    private static void permutation(String prefix, String str){
        int n = str.length();
        if (n == 0) 
            System.out.println(prefix);
        else {
            for (int i = 0; i < n; i++)
                permutation(prefix + str.charAt(i), 
            str.substring(0, i) + str.substring(i+1));
        }
    }
    public static void main(String[] args) {
        permutation("", "ABCD");
    }
}

但是当我们开始弹出堆栈时,我仍然无法获得该部分。例如,递归一直持续到置换(“ABCD”,“”),其中基本情况遇到并且它打印ABCD。但现在发生了什么?我们从函数调用堆栈中弹出置换(“ABC”,“D”)..我们用这个做什么等等。

请有人帮忙解释一下。

另外,我需要一些关于时间复杂度的指针?不像完整的计算,但有一些提示。

3 个答案:

答案 0 :(得分:2)

更简单的示例:permutation("", "ABC"),将空字符串表示为*

* ABC + A BC + AB C - ABC *
      |      |
      |      ` AC B - ACB *
      |
      + B AC + BA C - BAC *
      |      |
      |      ` BC A - BCA *
      |
      ` C AB + CA B - CAB *
             |
             ` CB A - CBA *

请注意,这看起来就像一棵树。实际上,它叫一棵树。当我们开始时,我们将进入("A", "BC")州;我们继续致电("AB", "C"),最后致电("ABC", "")。在这里我们打印输出。然后我们记得我们有未完成的业务,我们返回循环,但上一级的循环只有一个循环。所以我们也在那里完成,并返回("A", "BC")级别; "BC"中有两个元素,我们只完成了"B",所以它现在"C"转:我们称之为("AC", "B") },然后调用("ACB", "")。现在("A", "BC")下的所有循环都已完成......但是等等,仍然可以做!因为("", "ABC")还有两个要处理的字母。因此,我们通常称之为&#34;深度优先搜索&#34;分支机构。

在每个级别都是一个循环。循环收缩(我们在左侧的粗分支中迭代3次,然后在下一级中迭代2次,然后只有一次)但仍然存在循环。总的来说,我们进行n * (n - 1) * (n - 2) * ... * 2 * 1次迭代。 O(N!)

答案 1 :(得分:1)

作为一种递归,您可以使用 Stream.reduce 方法。首先为每个字符位置准备一个可能的字符组合的列表,然后连续减少这些列表的流为一个列表,通过对列表元素对求和。

作为列表元素,您可以使用 Map<Integer,String>,其中 key - 字符在字符串中的位置,value - 字符本身,以及总结这些映射的内容,不包括那些已经存在的

然后你会得到一个字符排列列表。

比如你有一个四字串????,那么你要经过三步约化,连续累加结果:

<头>
str1 + str2 = sum1 sum1 + str3 = sum2 sum2 + str4 = 总和
? + ? = ??
? + ? = ??
? + ? = ??
?? + ? = ???
?? + ? = ???
?? + ? = ???
?? + ? = ???
?? + ? = ???
?? + ? = ???
??? + ? = ????
??? + ? = ????
??? + ? = ????
??? + ? = ????
??? + ? = ????
??? + ? = ????
每个 15 字符的

4 和是 60 和,导致 4! = 24 排列。

Try it online!

// original string
String str = "????";
// array of characters of the string
int[] codePoints = str.codePoints().toArray();
// contents of the array
System.out.println(Arrays.toString(codePoints));
//[120276, 120277, 120278, 120279]
// list of permutations of characters
List<Map<Integer, String>> permutations = IntStream.range(0, codePoints.length)
        // Stream<List<Map<Integer,String>>>
        .mapToObj(i -> IntStream.range(0, codePoints.length)
                // represent each character as Map<Integer,String>
                .mapToObj(j -> Map.of(j, Character.toString(codePoints[j])))
                // collect a list of maps
                .collect(Collectors.toList()))
        // intermediate output
        //[{0=?}, {1=?}, {2=?}, {3=?}]
        //[{0=?}, {1=?}, {2=?}, {3=?}]
        //[{0=?}, {1=?}, {2=?}, {3=?}]
        //[{0=?}, {1=?}, {2=?}, {3=?}]
        .peek(System.out::println)
        // reduce a stream of lists to a single list
        .reduce((list1, list2) -> list1.stream()
                // summation of pairs of maps from two lists
                .flatMap(map1 -> list2.stream()
                        // filter out those keys that are already present
                        .filter(map2 -> map2.keySet().stream()
                                .noneMatch(map1::containsKey))
                        // join entries of two maps
                        .map(map2 -> new LinkedHashMap<Integer, String>() {{
                            putAll(map1);
                            putAll(map2);
                        }}))
                // collect into a single list
                .collect(Collectors.toList()))
        // List<Map<Integer,String>>
        .orElse(List.of(Map.of(0, str)));
// number of permutations
System.out.println(permutations.size()); // 24

// number of rows (factorial of string length - 1)
int rows = IntStream.range(1, codePoints.length)
        .reduce((a, b) -> a * b).orElse(1);

// column-wise output
IntStream.range(0, rows)
        .mapToObj(i -> IntStream.range(0, permutations.size())
                .filter(j -> j % rows == i)
                .mapToObj(permutations::get)
                .map(map -> map.toString().replace(" ", ""))
                .collect(Collectors.joining(" ")))
        .forEach(System.out::println);
//{0=?,1=?,2=?,3=?} {1=?,0=?,2=?,3=?} {2=?,0=?,1=?,3=?} {3=?,0=?,1=?,2=?}
//{0=?,1=?,3=?,2=?} {1=?,0=?,3=?,2=?} {2=?,0=?,3=?,1=?} {3=?,0=?,2=?,1=?}
//{0=?,2=?,1=?,3=?} {1=?,2=?,0=?,3=?} {2=?,1=?,0=?,3=?} {3=?,1=?,0=?,2=?}
//{0=?,2=?,3=?,1=?} {1=?,2=?,3=?,0=?} {2=?,1=?,3=?,0=?} {3=?,1=?,2=?,0=?}
//{0=?,3=?,1=?,2=?} {1=?,3=?,0=?,2=?} {2=?,3=?,0=?,1=?} {3=?,2=?,0=?,1=?}
//{0=?,3=?,2=?,1=?} {1=?,3=?,2=?,0=?} {2=?,3=?,1=?,0=?} {3=?,2=?,1=?,0=?}

另见:How do you check if a word has an anagram that is a palindrome?

答案 2 :(得分:1)

您可以使用 mapreduce 方法生成字符串的排列数组。

此解决方案假设原始字符串不包含重复字符,否则应使用置换映射 Map<Integer,String> 而不是置换数组 {{1 }}。

String[] 方法采用一对置换数组,并将它们的元素成对求和,累积结果。例如,对于一个五字符的字符串 reduce 的四步归约:

?????

Try it online!

0 [?, ?, ?, ?, ?]
1 [?, ?, ?, ?, ?]
---
sum1: [??, ??, ??, ??, ...]
2 [?, ?, ?, ?, ?]
---
sum2: [???, ???, ???, ...]
3 [?, ?, ?, ?, ?]
---
sum3: [????, ????, ...]
4 [?, ?, ?, ?, ?]
---
total: [?????, ?????, ...]
// original string
String str = "?????";
// array of characters of the string
int[] codePoints = str.codePoints().toArray();
String[] permutations = IntStream.range(0, codePoints.length)
        // intermediate output, character-position
        .peek(i -> System.out.print(i + " "))
        // prepare an array of possible permutations for each character-position
        .mapToObj(i -> Arrays.stream(codePoints).mapToObj(Character::toString)
                // array of characters as strings
                .toArray(String[]::new))
        // intermediate output, array of possible permutations
        .peek(arr -> System.out.println(Arrays.deepToString(arr)))
        // reduce a stream of arrays to a single array
        .reduce((arr1, arr2) -> Arrays.stream(arr1)
                // summation of pairs of strings from two arrays
                .flatMap(str1 -> Arrays.stream(arr2)
                        // filter out those characters that are already present
                        .filter(str2 -> !str1.contains(str2))
                        // concatenate two strings
                        .map(str2 -> str1 + str2))
                // collect into a single array
                .toArray(String[]::new))
        // otherwise an empty array
        .orElse(new String[0]);

中间输出,每个字符位置的可能排列数组:

// final output
System.out.println("Number of permutations: " + permutations.length);
// number of rows
int rows = permutations.length / 10;
// permutations by columns
IntStream.range(0, rows).forEach(i -> System.out.println(
        IntStream.range(0, permutations.length)
                .filter(j -> j % rows == i)
                .mapToObj(j -> permutations[j])
                .collect(Collectors.joining(" "))));

最终输出,按列排列:

0 [?, ?, ?, ?, ?]
1 [?, ?, ?, ?, ?]
2 [?, ?, ?, ?, ?]
3 [?, ?, ?, ?, ?]
4 [?, ?, ?, ?, ?]