Algorithm to find next greater permutation of a given string

Algorithm

Algorithm Problem Overview


I want an efficient algorithm to find the next greater permutation of the given string.

Algorithm Solutions


Solution 1 - Algorithm

Wikipedia has a nice article on lexicographical order generation. It also describes an algorithm to generate the next permutation.

Quoting:

The following algorithm generates the next permutation lexicographically after a given permutation. It changes the given permutation in-place.

> 1. Find the highest index i such that s[i] < s[i+1]. If no such index exists, the permutation is the last permutation. > 2. Find the highest index j > i such that s[j] > s[i]. Such a j must exist, since i+1 is such an index. > 3. Swap s[i] with s[j]. > 4. Reverse the order of all of the elements after index i till the last element.

Solution 2 - Algorithm

A great solution that works is described here: https://www.nayuki.io/page/next-lexicographical-permutation-algorithm. And the solution that, if next permutation exists, returns it, otherwise returns false:

function nextPermutation(array) {
    var i = array.length - 1;
    while (i > 0 && array[i - 1] >= array[i]) {
        i--;
    }
    
    if (i <= 0) {
        return false;
    }
    
    var j = array.length - 1;
    
    while (array[j] <= array[i - 1]) {
        j--;
    }
    
    var temp = array[i - 1];
    array[i - 1] = array[j];
    array[j] = temp;
    
    j = array.length - 1;
    
    while (i < j) {
        temp = array[i];
        array[i] = array[j];
        array[j] = temp;
        i++;
        j--;
    }
    
    return array;
}

Solution 3 - Algorithm

> Using the source cited by @Fleischpfanzerl

Next Lexicographical Permutation

We follow the steps as below to find the next lexicographical permutation:

enter image description here

nums = [0,1,2,5,3,3,0]
nums = [0]*5
curr = nums[-1]
pivot = -1
for items in nums[-2::-1]:
    if items >= curr:
        pivot -= 1
        curr = items
    else:
        break
if pivot == - len(nums):
    print('break')     # The input is already the last possible permutation

j = len(nums) - 1
while nums[j] <= nums[pivot - 1]:
    j -= 1
nums[j], nums[pivot - 1] = nums[pivot - 1], nums[j]
nums[pivot:] = nums[pivot:][::-1]

> [1, 3, 0, 2, 3, 5]

So the idea is: The idea is to follow steps -

  1. Find a index 'pivot' from the end of the array such that nums[i - 1] < nums[i]
  2. Find index j, such that nums[j] > nums[pivot - 1]
  3. Swap both these indexes
  4. Reverse the suffix starting at pivot

Solution 4 - Algorithm

Homework? Anyway, can look at the C++ function std::next_permutation, or this:

http://blog.bjrn.se/2008/04/lexicographic-permutations-using.html

Solution 5 - Algorithm

We can find the next largest lexicographic string for a given string S using the following step.

1. Iterate over every character, we will get the last value i (starting from the first character) that satisfies the given condition S[i] < S[i + 1]
2. Now, we will get the last value j such that S[i] < S[j]
3. We now interchange S[i] and S[j]. And for every character from i+1 till the end, we sort the characters. i.e., sort(S[i+1]..S[len(S) - 1])

The given string is the next largest lexicographic string of S. One can also use next_permutation function call in C++.

Solution 6 - Algorithm

nextperm(a, n)

1. find an index j such that a[j….n - 1] forms a monotonically decreasing sequence.
2. If j == 0 next perm not possible
3. Else 
    1. Reverse the array a[j…n - 1]
    2. Binary search for index of a[j - 1] in a[j….n - 1]
    3. Let i be the returned index
    4. Increment i until a[j - 1] < a[i]
    5. Swap a[j - 1] and a[i]


O(n) for each permutation.

Solution 7 - Algorithm

void Solution::nextPermutation(vector<int> &a) {    
int i, j=-1, k, n=a.size();
for(i=0; i<n-1; i++) if(a[i] < a[i+1]) j=i;
if(j==-1) reverse(a.begin(), a.end());
else {
    for(i=j+1; i<n; i++) if(a[j] < a[i]) k=i;
    swap(a[j],a[k]);
    reverse(a.begin()+j+1, a.end());
}}

Solution 8 - Algorithm

I came across a great tutorial. link : https://www.youtube.com/watch?v=quAS1iydq7U

void Solution::nextPermutation(vector<int> &a) {


int k=0;
int n=a.size();
for(int i=0;i<n-1;i++)
{
	if(a[i]<a[i+1])
	{
		k=i;
	}
}

int ele=INT_MAX;
int pos=0;
for(int i=k+1;i<n;i++)
{
	if(a[i]>a[k] && a[i]<ele)
	{
		ele=a[i];pos=i;
	}
		
}

if(pos!=0)
{

swap(a[k],a[pos]);

reverse(a.begin()+k+1,a.end());	

}    

}

Solution 9 - Algorithm

A great solution that works is described here: https://www.nayuki.io/page/next-lexicographical-permutation-algorithm. and if you are looking for

source code:

/**
	 * method to find the next lexicographical greater string
	 * 
	 * @param w
	 * @return a new string
	 */
	static String biggerIsGreater(String w) {
		char charArray[] = w.toCharArray();
		int n = charArray.length;
		int endIndex = 0;

		// step-1) Start from the right most character and find the first character
		// that is smaller than previous character.
		for (endIndex = n - 1; endIndex > 0; endIndex--) {
			if (charArray[endIndex] > charArray[endIndex - 1]) {
				break;
			}
		}

		// If no such char found, then all characters are in descending order
		// means there cannot be a greater string with same set of characters
		if (endIndex == 0) {
			return "no answer";
		} else {
			int firstSmallChar = charArray[endIndex - 1], nextSmallChar = endIndex;

			// step-2) Find the smallest character on right side of (endIndex - 1)'th
			// character that is greater than charArray[endIndex - 1]
			for (int startIndex = endIndex + 1; startIndex < n; startIndex++) {
				if (charArray[startIndex] > firstSmallChar && charArray[startIndex] < charArray[nextSmallChar]) {
					nextSmallChar = startIndex;
				}
			}

			// step-3) Swap the above found next smallest character with charArray[endIndex - 1]
			swap(charArray, endIndex - 1, nextSmallChar);

			// step-4) Sort the charArray after (endIndex - 1)in ascending order
			Arrays.sort(charArray, endIndex , n);

		}
		return new String(charArray);
	}

	/**
	 * method to swap ith character with jth character inside charArray
	 * 
	 * @param charArray
	 * @param i
	 * @param j
	 */
	static void swap(char charArray[], int i, int j) {
		char temp = charArray[i];
		charArray[i] = charArray[j];
		charArray[j] = temp;
	}

If you are looking for video explanation for the same, you can visit here.

Solution 10 - Algorithm

This problem can be solved just by using two simple algorithms searching and find smaller element in just O(1) extra space and O(nlogn ) time and also easy to implement .

To understand this approach clearly . Watch this Video : https://www.youtube.com/watch?v=DREZ9pb8EQI

Solution 11 - Algorithm

def result(lst):
    if len(lst) == 0:
        return 0
    if len(lst) == 1:
        return [lst]
    l = []
    for i in range(len(lst)):
        m = lst[i]
        remLst = lst[:i] + lst[i+1:]
        for p in result(remLst):
            l.append([m] + p)
    return l
result(['1', '2', '3'])

Solution 12 - Algorithm

  1. Start traversing from the end of the list. Compare each one with the previous index value.

  2. If the previous index (say at index i-1) value, consider x, is lower than the current index (index i) value, sort the sublist on right side starting from current position i.

  3. Pick one value from the current position till end which is just higher than x, and put it at index i-1. At the index the value was picked from, put x. That is:

    swap(list[i-1], list[j]) where j >= i, and the list is sorted from index "i" onwards

Code:

public void nextPermutation(ArrayList<Integer> a) {
    for (int i = a.size()-1; i > 0; i--){
        if (a.get(i) > a.get(i-1)){
            Collections.sort(a.subList(i, a.size()));
            for (int j = i; j < a.size(); j++){
                if (a.get(j) > a.get(i-1)) {
                    int replaceWith = a.get(j); // Just higher than ith element at right side.
                    a.set(j, a.get(i-1));
                    a.set(i-1, replaceWith);
                    return;
                }
            }
        }
    }
    // It means the values are already in non-increasing order. i.e. Lexicographical highest
    // So reset it back to lowest possible order by making it non-decreasing order.
    for (int i = 0, j = a.size()-1; i < j; i++, j--){
        int tmp = a.get(i);
        a.set(i, a.get(j));
        a.set(j, tmp);
    }
}

Example :

10 40 30 20 => 20 10 30 40  // 20 is just bigger than 10

10 40 30 20 5 => 20 5 10 30 40 // 20 is just bigger than 10.  Numbers on right side are just sorted form of this set {numberOnRightSide - justBigger + numberToBeReplaced}.

Solution 13 - Algorithm

This is efficient enough up to strings with 11 letters.

// next_permutation example
#include <iostream>     
#include <algorithm>
#include <vector>
using namespace std;

void nextPerm(string word) {
  vector<char> v(word.begin(), word.end());
  vector<string> permvec; // permutation vector
  string perm;
  int counter = 0;  // 
  int position = 0; // to find the position of keyword in the permutation vector

  sort (v.begin(),v.end());
  
  do {
  	perm = "";
    for (vector<char>::const_iterator i = v.begin(); i != v.end(); ++i) {
    	perm += *i;
    }
    permvec.push_back(perm); // add permutation to vector
    
    if (perm == word) {
    	position = counter +1; 
    }
    counter++;
    
  } while (next_permutation(v.begin(),v.end() ));
  
  if (permvec.size() < 2 || word.length() < 2) {
  	cout << "No answer" << endl;
  }
  else if (position !=0) {
	cout << "Answer: " << permvec.at(position) << endl;
  }

}

int main () {
  string word = "nextperm";
  string key = "mreptxen";  
  nextPerm(word,key); // will check if the key is a permutation of the given word and return the next permutation after the key.
  
  return 0;
}

Solution 14 - Algorithm

I hope this code might be helpful.

int main() {

    char str[100];
    cin>>str;
    int len=strlen(len);
    int f=next_permutation(str,str+len);    
    if(f>0) {
        print the string
    } else {
        cout<<"no answer";
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionrkbView Question on Stackoverflow
Solution 1 - AlgorithmAlexandruView Answer on Stackoverflow
Solution 2 - AlgorithmrishatView Answer on Stackoverflow
Solution 3 - AlgorithmHello.WorldView Answer on Stackoverflow
Solution 4 - AlgorithmBrianView Answer on Stackoverflow
Solution 5 - AlgorithmgauravView Answer on Stackoverflow
Solution 6 - Algorithmuser3809584View Answer on Stackoverflow
Solution 7 - AlgorithmShubham AggarwalView Answer on Stackoverflow
Solution 8 - AlgorithmexecutableView Answer on Stackoverflow
Solution 9 - AlgorithmKanahaiyaView Answer on Stackoverflow
Solution 10 - Algorithmvishesh jainView Answer on Stackoverflow
Solution 11 - AlgorithmJayendra MishraView Answer on Stackoverflow
Solution 12 - AlgorithmSaurav SahuView Answer on Stackoverflow
Solution 13 - AlgorithmBahadır ÖzkanView Answer on Stackoverflow
Solution 14 - AlgorithmsacView Answer on Stackoverflow