criskgl
8/16/2019 - 8:08 PM

Get permutations in ascending lexicographic order

Get string permutations in ascending lexicographic order

Code short description

The code has been divided into 4 functions each one representing a step to find the next lexiccographic permutation of a given string

The mapAll function just iterates over and over computing the next lexicographic permutation until going back to the original string

Steps

  • Find FIRST : rightmost character in given string, which is smaller than its next character
  • Find CEILING : smallest character on right of FIRST , which is greater than FIRST
  • SWAP elements found in steps above
  • ORDER the substring (in ascending order) after the original index of FIRST.
package Strings;

import java.util.Arrays;

public class StringLexicographicMap {
	public static void main(String[] args) {
		String input = "ABBCC";
		
		//String result = lexicographicMapNext(input);
		//System.out.println(result);
		
		lexicographicMapAll(input);
	}
	
	public static void lexicographicMapAll(String s){
		String result = s;
		do{
			result = lexicographicMapNext(result);
			System.out.println(result);
		}while(result != null);
	}
	
	public static String lexicographicMapNext(String s){
		char [] init = s.toCharArray();
		//1. FIND FIRST
		int  firstIndex = findFirst(init);
		//check if we got an string that is the last in lexicographic order. 
		//Another way to word it is to say that if we find its next 
		//we would go back to a perfectly sorted array
		if(firstIndex == -1) return null;
		char firstElem = init[firstIndex];
		//2. FIND CEILING OF FIRST
		int ceilingIndex = findCeiling(init, firstIndex);
		char ceilingElem = init[ceilingIndex];
		//3. SWAP FIRST AND CEILING
		init[firstIndex] = ceilingElem;
		init[ceilingIndex] = firstElem;
		//4. SORT SUBSTRING (ASCENDING) AFTER FIRST CHARACTER
		String result = sortAfterFirst(init, firstIndex);
		return result;
	}
	
	//FIND FIRST FUNCTION 
	//Finds rightmost character that is smaller than its next character
	//---------------------------------------------------------------------
	//returns the index of the character that satisfies the above condition
	//returns -1 when this condition never happens (e.g ZBA)
	public static int findFirst(char[] a){
		for(int i = a.length - 2; i >= 0; i--){
			char current = a[i];
			char currentNext = a[i + 1];
			if((int)current < (int) currentNext){
				return i;
			}
		}
		return -1;
	}
	//FIND CEILING FUNCTION
	//Finds the smallest character on right of FIRST character 
	//which is greater than FIRST character
	//---------------------------------------------------------------------
	//returns the index of the ceiling character
	public static int findCeiling(char[] a, int firstIndex){
		char first = a[firstIndex];
		char smallest = a[firstIndex + 1];
		int smallestIndex = firstIndex + 1;
		for(int i = firstIndex + 2; i < a.length; i++){
			if(a[i] < smallest && a[i] > first){
				smallest = a[i];
				smallestIndex = i;
			}
		}
		return smallestIndex;
	}
	//SORT ELEMENTS AFTER INDEX OR FIRST *ORIGINAL* INDEX
	//---------------------------------------------------------------------
	//returns a string with elements ordered after first ORIGINAL index
	public static String sortAfterFirst(char[] a, int firstIndex){
		StringBuilder bob = new StringBuilder();
		for(char c : a){
			bob.append(c);
		}
		String init = bob.toString();
		String toOrder = init.substring(firstIndex+1);
		char [] toOrderArr = toOrder.toCharArray();
		Arrays.sort(toOrderArr);
		StringBuilder bob2 = new StringBuilder();
		bob2.append(init.substring(0, firstIndex + 1));
		for(char c : toOrderArr){
			bob2.append(c);
		}
		return bob2.toString();
	}
}