76

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

Ðаn
  • 10,934
  • 11
  • 59
  • 95
rkb
  • 10,933
  • 22
  • 76
  • 103
  • 1
    http://stackoverflow.com/questions/352203/generating-permutations-lazily – Brian Carper Oct 26 '09 at 00:25
  • 12
    What does `the next greater permutation` mean? I came from Leetcode, want to search the meaning of this thing. – JW.ZG Nov 16 '16 at 02:20
  • 5
    @JW.ZG Given a number n, find the smallest number that has same set of digits as n and is greater than n. https://www.geeksforgeeks.org/find-next-greater-number-set-digits/ – nichijou Dec 05 '18 at 15:19
  • To phrase this question more formally: A string (or any kind of sequence) has a length ℓ, and has 2 to the power ℓ [permutations](//en.wikipedia.org/wiki/Permutation). E.g. string `"abc"` has permutations `"abc"`, `"acb"`, `"bac"`, `"bca"`, `"cab"`, and `"cba"`. Strings can be [lexicographically ordered](//en.wikipedia.org/wiki/Lexicographic_order), e.g. `"acb"` would come before `"cab"` but after `"abc"` in a dictionary. My example list is lexicographically ordered. The _next greater_ permutation is the one that would appear _earliest_ in the dictionary, but _after_ the given permutation. – Sebastian Simon Nov 05 '22 at 04:03

14 Answers14

169

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.
thisisashwani
  • 1,748
  • 2
  • 18
  • 25
Alexandru
  • 25,070
  • 18
  • 69
  • 78
  • 24
    for those who are wondering why the step 4 is not sort: step 1 already implies that from s[i+1] to the end it is already in the descending order, hence reverse is equivalent to sort – Ted Xu Jan 04 '16 at 08:48
  • Step 4 is just to make sure that string is next least array. For ex .. for arr= [6, 5, 4, 3, 2, 3, 2, 1, 0].. up to step 3 it will be [6, 5, 4, 3, 3, 2 , 2, 1, 0] and step 4 will result to [6, 5, 4, 3, 3, 0, 1, 2, 2] which is expected. – Kunal Kishore May 29 '20 at 08:48
  • How does it go for [9,5,8,2]? What is the value of `j`? – Suhail Gupta Mar 11 '21 at 08:56
  • I had the same idea, but it doesn't work for "dkhc". it would return "kcdh" but the correct answer is "hcdk". – mh-firouzjah Dec 09 '21 at 05:48
24

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;
}
Nayuki
  • 17,911
  • 6
  • 53
  • 80
rishat
  • 8,206
  • 4
  • 44
  • 69
20

Using the source cited by @Fleischpfanzerl:

enter image description here

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

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
Nayuki
  • 17,911
  • 6
  • 53
  • 80
Hello.World
  • 720
  • 8
  • 22
6

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

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

Brian
  • 117,631
  • 17
  • 236
  • 300
2

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++.

Anindya Dutta
  • 1,972
  • 2
  • 18
  • 33
gaurav
  • 31
  • 3
1

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.
1
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());
}}
1

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()); 

}    

}

executable
  • 180
  • 1
  • 12
0

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.

Kanahaiya
  • 406
  • 2
  • 11
0

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

0
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'])
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
-1
  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}.
Saurav Sahu
  • 13,038
  • 6
  • 64
  • 79
-1

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

-8

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";
    }
}
Anindya Dutta
  • 1,972
  • 2
  • 18
  • 33
sac
  • 137
  • 2
  • 5
  • 15