【问题标题】:Recursively printing only lexicographically larger substrings递归打印仅按字典顺序较大的子字符串
【发布时间】:2020-12-17 11:26:52
【问题描述】:

我正在编写一个代码,用于递归地仅打印字符串的字典顺序较大的子字符串。

static ArrayList<String> getPermutations(String str) {
    if (str.length() == 0) {
        ArrayList<String> tempArrayList = new ArrayList<String>();
        tempArrayList.add("");
        return tempArrayList;
    }

    char currChar = str.charAt(0);
    ArrayList<String> resultArrayList = new ArrayList<String>();
    ArrayList<String> recResult = getPermutations(str.substring(1));

    for (String j : recResult) {
        for (int i = 0; i < str.length(); i++) {
            resultArrayList.add(j.substring(0, i) + currChar + j.substring(i));
        }
    }
    return resultArrayList;
}

public static void main(String args[]) {
    Scanner sc = new Scanner(System.in);
    String str = sc.nextLine();
    ArrayList<String> al = getPermutations(str);
    Collections.sort(al);
    // System.out.println(al.toString());
    int index = al.lastIndexOf(str);
    while (index < al.size() - 1) {
        System.out.println(al.get(index + 1));
        index++;
    }
}

代码运行良好。我在这里所做的是递归生成所有子字符串并同时将它们插入到 ArrayList 中。后来对该列表进行了排序,比较了字符串并完成了。

现在困扰我的是这个程序的复杂性。在这里,我生成所有子字符串,然后从中进行选择。对于递归,我觉得它是一种自动化过程,所有子字符串都必须至少创建或访问一次。 所以,在这一点上,我想问一下这是否可以像在递归函数中进行某种检查一样进行优化,以便只创建所需的子字符串(按字典顺序更大)。如果是,请详细说明在基于递归的解决方案的情况下如何考虑。

【问题讨论】:

  • 您好!欢迎来到 Stackoverflow。您能否解释一下“字符串的字典顺序较大的子字符串”是什么意思?你可以举一个字符串的例子,以及它的字典上更大的子字符串。我很困惑,因为我假设在所有可能的子字符串中,必须有一个字典顺序最大的子字符串(两个字符串相等,或者一个大于另一个)。
  • 当然。例如,“bac”的子字符串是“abc”、“acb”、“bac”、“bca”、“cab”和“cba”,但对于字典顺序较大或按字典顺序,只有子字符串“bca”、“ cab" 和 "cba" 将打印为 "bac" 作为输入。希望这可以澄清它。

标签: java recursion substring permutation lexicographic


【解决方案1】:

我会这样做,没有递归,没有额外的字符串被构建和忽略,没有由输入中的重复字母引起的重复。

希望代码中的cmets足以理解逻辑。

static List<String> getLargerPermutations(String input) {
    // Build ordered array of unique characters in the input string
    // E.g. "mississippi" -> ['i', 'm', 'p', 's']
    char[] buf = input.toCharArray();
    Set<Character> charSet = new TreeSet<>();
    for (char ch : buf)
        charSet.add(ch);
    Character[] chars = charSet.toArray(Character[]::new);
    
    // Build map of character to index into CharCount array
    // E.g. "mississippi" -> { i=0, m=1, p=2, s=3 }
    Map<Character, Integer> charIndex = new HashMap<>();
    for (int i = 0; i < chars.length; i++)
        charIndex.put(chars[i], i);
    
    // Build position indexes with starting index for input string
    // E.g. "mississippi" -> [1, 0, 3, 3, 0, 3, 3, 0, 2, 2, 0]
    int[] idx = new int[buf.length];
    for (int i = 0; i < buf.length; i++)
        idx[i] = charIndex.get(buf[i]);
    
    // Build permutations by "incrementing" the indexes
    // E.g. "mississippi" -> [1, 0, 3, 3, 0, 3, 3, 0, 2, 2, 0] (starting buffer, not returned)
    //                    -> [1, 0, 3, 3, 0, 3, 3, 2, 0, 0, 2]
    //                    -> [1, 0, 3, 3, 0, 3, 3, 2, 0, 2, 0]
    //                    -> [1, 0, 3, 3, 0, 3, 3, 2, 2, 0, 0]
    //                    -> [1, 0, 3, 3, 2, 0, 0, 0, 2, 3, 3]
    //                    -> [1, 0, 3, 3, 2, 0, 0, 0, 3, 2, 3]
    List<String> permutations = new ArrayList<>();
    int[] counts = new int[chars.length];
    OUTER: for (int i = idx.length - 1; i >= 0; i--) { // keep going backwards to advance character at previous position:
        counts[idx[i]]++;                              //   return character at current position to pool
        while (++idx[i] < chars.length) {              //   try next character at current position:
            if (counts[idx[i]] > 0) {                  //     if character is available:
                counts[idx[i]]--;                      //       take character from pool
                buf[i] = chars[idx[i]];                //       place character in buffer
                i++;                                   //       advance to next position
                if (i == buf.length) {                 //       if buffer full:
                    permutations.add(new String(buf)); //         we have a good permutation
                    continue OUTER;                    //         continue outer loop to advance to next permutation
                }
                idx[i] = -1;                           //       clear search index of next position
            }
        }                                              //   loop back to try next character
    }                                                  // out of characters at current position, so loop back to try previous position
    return permutations;
}

测试

getLargerPermutations("ball").forEach(System.out::println);

使用"ball"输出

blal
blla
labl
lalb
lbal
lbla
llab
llba

使用"powwow"输出

powwwo
pwooww
pwowow
pwowwo
pwwoow
pwwowo
pwwwoo
woopww
woowpw
woowwp
wopoww
wopwow
wopwwo
wowopw
wowowp
wowpow
wowpwo
wowwop
wowwpo
wpooww
wpowow
wpowwo
wpwoow
wpwowo
wpwwoo
wwoopw
wwoowp
wwopow
wwopwo
wwowop
wwowpo
wwpoow
wwpowo
wwpwoo
wwwoop
wwwopo
wwwpoo

【讨论】:

  • 感谢分享。我可以看到这是一个非常有效的代码来做这件事。唯一的事情是我只是想知道我的递归代码是否可以进一步优化,同时保持其递归性质。但是,如果我不得不在没有递归的情况下也这样做的话,你编写的代码对我来说是非常有知识的。
猜你喜欢
  • 1970-01-01
  • 2020-08-05
  • 1970-01-01
  • 2021-06-24
  • 2018-05-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-03
相关资源
最近更新 更多