0

I was writing a code for printing only the lexicographically larger substrings of a string recursively.

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++;
    }
}

The code is working fine. What I've done here is recursively generated all the substrings and simultaneously inserted them in an ArrayList. Later sorted that list, compared the strings and voilà it was done.

Now what is bothering me is the complexity of this program. Here I am generating all the substrings and then choosing from them. For recursion, I feel that it's kind of an automated process, all the substrings will have to be created or visited at least once. So, at this point, I want to ask if this can be optimized like having some kind of check in the recursive function so that only the required substrings (lexicographically larger) are created. If yes, then please elaborate on how to think about it in the case of recursion based solutions.

Nitish
  • 13
  • 4
  • Hello! Welcome to Stackoverflow. Can you please explain what you mean by "lexicographically larger substrings of a string"? You could give an example of a string, and its lexicographically larger substrings. I am confused because I assume out of all possible substrings, there must be a lexicographically largest substring (either two strings are equal, or one is larger than the other). – Stef Aug 28 '20 at 15:21
  • Sure. For example, the substrings for "bac" are "abc", "acb", "bac", "bca", "cab" and "cba" but for lexicographically larger or in dictionary order, the only substrings "bca" , "cab" and "cba" will be printed for "bac" as input. Hope this clarifies it. – Nitish Aug 29 '20 at 14:29

1 Answers1

0

I would do it like this, no recursion, no extra strings built and ignored, no repeats caused by duplicate letters in the input.

Hopefully the comments in the code is enough to understand the logic.

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;
}

Test

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

Output with "ball"

blal
blla
labl
lalb
lbal
lbla
llab
llba

Output with "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
Andreas
  • 154,647
  • 11
  • 152
  • 247
  • Thanks for sharing this. I can see that it is quite an efficient code to do the thing. The only thing is that I just wanted to know if my recursive code could be optimized further or not whilst maintaining its recursive nature. But the code you wrote was quite knowledgeable for me if I had to do it without recursion also. – Nitish Aug 29 '20 at 14:41