Given a string s consisting of distinct lowercase characters, find its rank among all its permutations when sorted lexicographically.
Examples:
Input: s = "acb"
Output: 2
Explanation: If all the permutations of the string are arranged lexicographically they will be "abc", "acb", "bac", "bca", "cab", "cba". From here it can be clearly seen that the rank of s is 2.Input: s = "string"
Output: 598Input: s = "cba"
Output: Rank = 6
Table of Content
[Naive Approach] Generating all permutations - O(n * n!) Time and O(n) Space
The idea is to generate all the permutations in lexicographic order and store the rank of the current string. To generate all permutations, we first sort the string and then one by one generate lexicographically next permutation. After generating a permutation, check if the generated permutation is the same as the given string and return the rank of string.
Dry run for s = "bca":
- Original = "bca", after sorting the string becomes "abc" and initial rank is 1
- First permutation "abc" does not match "bca", next permutation becomes "acb" and rank becomes 2
- Second permutation "acb" does not match "bca", next permutation becomes "bac" and rank becomes 3
- Third permutation "bac" does not match "bca", next permutation becomes "bca" and rank becomes 4
- "bca" matches the original string, so the process stops and final rank = 4
#include <algorithm>
#include <vector>
using namespace std;
long long findRank(string &s) {
// Create a copy of the original
// string to keep it unchanged
string original = s;
// Sort the string to get the first
// permutation in lexicographic order
sort(s.begin(), s.end());
long long rank = 1;
// Keep generating next permutation
// until we find the original string
while (s != original) {
next_permutation(s.begin(), s.end());
rank++;
}
return rank;
}
int main() {
string s = "string";
cout << findRank(s);
return 0;
}
import java.util.Arrays;
class GfG {
static long findRank(String s) {
// Create a copy of the original
// string to keep it unchanged
String original = s;
// Convert to character array for sorting and permutation
char[] arr = s.toCharArray();
// Sort the string to get the first
// permutation in lexicographic order
Arrays.sort(arr);
long rank = 1;
// Keep generating next permutation
// until we find the original string
while (!String.valueOf(arr).equals(original)) {
nextPermutation(arr);
rank++;
}
return rank;
}
static void nextPermutation(char[] arr) {
int i = arr.length - 2;
while (i >= 0 && arr[i] >= arr[i + 1]) i--;
if (i >= 0) {
int j = arr.length - 1;
while (arr[j] <= arr[i]) j--;
char temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
reverse(arr, i + 1, arr.length - 1);
}
static void reverse(char[] arr, int start, int end) {
while (start < end) {
char temp = arr[start];
arr[start++] = arr[end];
arr[end--] = temp;
}
}
public static void main(String[] args) {
String s = "string";
System.out.println(findRank(s));
}
}
def next_permutation(arr):
# Find the rightmost element that is smaller than its next
i = len(arr) - 2
while i >= 0 and arr[i] >= arr[i + 1]:
i -= 1
# If no such element, we're at the last permutation
if i < 0:
return False
# Find the smallest element greater than arr[i] to its right
j = len(arr) - 1
while arr[j] <= arr[i]:
j -= 1
# Swap them
arr[i], arr[j] = arr[j], arr[i]
# Reverse the suffix after position i
arr[i + 1:] = reversed(arr[i + 1:])
return True
def findRank(s):
# Create a copy of the original
# string to keep it unchanged
original = s
# Sort the string to get the first
# permutation in lexicographic order
arr = sorted(list(s))
rank = 1
# Keep generating next permutation
# until we find the original string
while ''.join(arr) != original:
next_permutation(arr)
rank += 1
return rank
if __name__ == "__main__":
s = "string"
print(findRank(s))
using System;
class GfG {
static long findRank(string s) {
// Create a copy of the original
// string to keep it unchanged
string original = s;
// Sort the string to get the first
// permutation in lexicographic order
char[] arr = s.ToCharArray();
Array.Sort(arr);
long rank = 1;
// Keep generating next permutation
// until we find the original string
while (new string(arr) != original) {
nextPermutation(arr);
rank++;
}
return rank;
}
static void nextPermutation(char[] arr) {
int i = arr.Length - 2;
while (i >= 0 && arr[i] >= arr[i + 1]) i--;
if (i >= 0) {
int j = arr.Length - 1;
while (arr[j] <= arr[i]) j--;
char temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
reverse(arr, i + 1, arr.Length - 1);
}
static void reverse(char[] arr, int start, int end) {
while (start < end) {
char temp = arr[start];
arr[start++] = arr[end];
arr[end--] = temp;
}
}
static void Main(string[] args) {
string s = "string";
Console.WriteLine(findRank(s));
}
}
function findRank(s) {
// Create a copy of the original
// string to keep it unchanged
let original = s;
// Sort the string to get the first
// permutation in lexicographic order
let arr = s.split('').sort();
let rank = 1;
// Keep generating next permutation
// until we find the original string
while (arr.join('') !== original) {
nextPermutation(arr);
rank++;
}
return rank;
}
function nextPermutation(arr) {
let i = arr.length - 2;
while (i >= 0 && arr[i] >= arr[i + 1]) i--;
if (i >= 0) {
let j = arr.length - 1;
while (arr[j] <= arr[i]) j--;
[arr[i], arr[j]] = [arr[j], arr[i]];
}
reverse(arr, i + 1, arr.length - 1);
}
function reverse(arr, start, end) {
while (start < end) {
[arr[start], arr[end]] = [arr[end], arr[start]];
start++;
end--;
}
}
// Driver Code
let s = "string";
console.log(findRank(s));
Output
598
[Better Approach] Count Smaller Strings - O(n^2) Time and O(1) Space
We count smaller strings than the given string, and at the end return rank as one plus the count value.
Rank = Count of Smaller + 1
For example, for "cba", the 5 smaller strings are "abc", "acb", "bac", "bca" and "cab" and our result is 5 + 1.
How do we count smaller?
We first find count of smaller strings when the first character is replaced. For example, for "cba", if we fix the first character other than "c", we get 4 smaller strings. Now we do the same thing for second character which means, we count smaller strings when first is "c" and second is smaller than "b". We have 1 such string.
For a better understanding follow the below illustration.
Let the given string be "string". In the input string, 's' is the first character. There are total 6 characters and 4 of them are smaller than 's'. So there can be 4 * 5! smaller strings where first character is smaller than 's', like followingÂ
- g x x x x x
- r x x x x x
- i x x x x x
- n x x x x x
Similarly we can use the same process for the other letters. Fix 's' and find the smaller strings starting with 's'.Â
- Repeat the same process for t, rank is 4*5! + 4*4! +. . .
- Now fix t and repeat the same process for r, rank is 4*5! + 4*4! + 3*3! + . . .
- Now fix r and repeat the same process for i, rank is 4*5! + 4*4! + 3*3! + 1*2! + . . .
- Now fix i and repeat the same process for n, rank is 4*5! + 4*4! + 3*3! + 1*2! + 1*1! + . . .
- Now fix n and repeat the same process for g, rank is 4*5! + 4*4! + 3*3! + 1*2! + 1*1! + 0*0!
If this process is continued the rank = 4*5! + 4*4! + 3*3! + 1*2! + 1*1! + 0*0! = 597. The above computations find count of smaller strings. Therefore rank of given string is count of smaller strings plus 1. The final rank = 1 + 597 = 598
Follow the steps mentioned below to implement the idea:
- Iterate through the string from index i = 0 to n - 1.
- For each character, count how many characters to its right are smaller than the current character.
- For each such smaller character, calculate how many permutations can be formed with the remaining characters.
- Add this value to the current rank.
- After completing the iteration, add 1 to the final rank and return it as the answer.
#include <iostream>
using namespace std;
// Factorial
long long fact(int n) {
long res = 1;
for (int i = 2; i <= n; i++) { res *= i; }
return res;
}
// A utility function to count
// smaller characters on right of arr[low]
int findSmallerInRight(string s, int low) {
int countRight = 0;
for (int i = low + 1; i < s.size(); ++i)
if (s[i] < s[low])
++countRight;
return countRight;
}
// A function to find rank of a string
// in all permutations of characters
long long findRank(string s) {
int n = s.size();
// 1 to be added to smaller count
long long rank = 1;
int countRight;
for (int i = 0; i < n; ++i) {
// Count number of chars smaller than str[i]
// from str[i+1] to str[len-1]
countRight = findSmallerInRight(s, i);
rank += countRight * fact(n - i - 1);
}
return rank;
}
int main() {
string s = "string";
cout << findRank(s);
return 0;
}
class GfG {
// Factorial
static long fact(int n) {
long res = 1;
for (int i = 2; i <= n; i++) { res *= i; }
return res;
}
// A utility function to count
// smaller characters on right of arr[low]
static int findSmallerInRight(String s, int low) {
int countRight = 0;
for (int i = low + 1; i < s.length(); ++i)
if (s.charAt(i) < s.charAt(low))
++countRight;
return countRight;
}
// A function to find rank of a string
// in all permutations of characters
static long findRank(String s) {
int n = s.length();
// 1 to be added to smaller count
long rank = 1;
int countRight;
for (int i = 0; i < n; ++i) {
// Count number of chars smaller than str[i]
// from str[i+1] to str[len-1]
countRight = findSmallerInRight(s, i);
rank += countRight * fact(n - i - 1);
}
return rank;
}
public static void main(String[] args) {
String s = "string";
System.out.println(findRank(s));
}
}
# Factorial
def fact(n):
res = 1
for i in range(2, n + 1):
res *= i
return res
# A utility function to count
# smaller characters on right of arr[low]
def findSmallerInRight(s, low):
countRight = 0
for i in range(low + 1, len(s)):
if s[i] < s[low]:
countRight += 1
return countRight
# A function to find rank of a string
# in all permutations of characters
def findRank(s):
n = len(s)
# 1 to be added to smaller count
rank = 1
for i in range(n):
# Count number of chars smaller than str[i]
# from str[i+1] to str[len-1]
countRight = findSmallerInRight(s, i)
rank += countRight * fact(n - i - 1)
return rank
if __name__ == "__main__":
s = "string"
print(findRank(s))
using System;
class GfG {
// Factorial
static long fact(int n) {
long res = 1;
for (int i = 2; i <= n; i++) { res *= i; }
return res;
}
// A utility function to count
// smaller characters on right of arr[low]
static int findSmallerInRight(string s, int low) {
int countRight = 0;
for (int i = low + 1; i < s.Length; ++i)
if (s[i] < s[low])
++countRight;
return countRight;
}
// A function to find rank of a string
// in all permutations of characters
static long findRank(string s) {
int n = s.Length;
// 1 to be added to smaller count
long rank = 1;
int countRight;
for (int i = 0; i < n; ++i) {
// Count number of chars smaller than str[i]
// from str[i+1] to str[len-1]
countRight = findSmallerInRight(s, i);
rank += countRight * fact(n - i - 1);
}
return rank;
}
static void Main(string[] args) {
string s = "string";
Console.WriteLine(findRank(s));
}
}
// Factorial
function fact(n) {
let res = 1;
for (let i = 2; i <= n; i++) { res *= i; }
return res;
}
// A utility function to count
// smaller characters on right of arr[low]
function findSmallerInRight(s, low) {
let countRight = 0;
for (let i = low + 1; i < s.length; ++i)
if (s[i] < s[low])
++countRight;
return countRight;
}
// A function to find rank of a string
// in all permutations of characters
function findRank(s) {
let n = s.length;
// 1 to be added to smaller count
let rank = 1;
let countRight;
for (let i = 0; i < n; ++i) {
// Count number of chars smaller than str[i]
// from str[i+1] to str[len-1]
countRight = findSmallerInRight(s, i);
rank += countRight * fact(n - i - 1);
}
return rank;
}
// Driver Code
let s = "string";
console.log(findRank(s));
Output
598
Note: We can avoid repeated computation of factorial by first calculating n! and then successively dividing by (n - i) to obtain the remaining factorial values during iteration.
[Expected Approach] Using Frequency Array - O(n) Time and O(1) Space
Create a frequency array to store character counts and convert it into a cumulative array to efficiently find how many characters are smaller than the current character, updating it after each index during iteration.
Dry run for s = "string":
- i = 0, character 's', smaller characters are g, i, n, r so count = 4, remaining = 5! = 120, rank becomes 1 + (4 × 120) = 481
- i = 1, character 't', smaller characters are g, i, n, r so count = 4, remaining = 4! = 24, rank becomes 481 + (4 × 24) = 577
- i = 2, character 'r', smaller characters are g, i, n so count = 3, remaining = 3! = 6, rank becomes 577 + (3 × 6) = 595
- i = 3, character 'i', smaller character is g so count = 1, remaining = 2! = 2, rank becomes 595 + (1 × 2) = 597
- i = 4, character 'n', smaller character is g so count = 1, remaining = 1! = 1, rank becomes 597 + (1 × 1) = 598
- i = 5, character 'g', smaller characters = 0, remaining = 0! = 1, rank remains 598
#include <iostream>
using namespace std;
// Factorial
long long fact(int n) {
long long res = 1;
for (int i = 2; i <= n; i++) { res *= i; }
return res;
}
// A function to find rank of a string in all permutations
// of characters
long long findRank(string &s)
{
int n = s.size();
long long mul = fact(n);
long long rank = 1;
// Using a vector of size 26 for lowercase letters
vector<int> count(26, 0);
// Populate the count array for each character in string
for (int i = 0; i < n; ++i) {
++count[s[i] - 'a'];
}
// Convert count to cumulative sum
for (int i = 1; i < 26; ++i) {
count[i] += count[i - 1];
}
for (int i = 0; i < n; ++i) {
mul /= (n - i);
// Get index of current character in count array
int charIndex = s[i] - 'a';
// Add count of characters smaller than current character
if (charIndex > 0) {
rank += count[charIndex - 1] * mul;
}
// Update count array
for (int j = charIndex; j < 26; ++j) {
--count[j];
}
}
return rank;
}
int main() {
string s = "string";
cout << findRank(s);
return 0;
}
class GfG {
// Factorial
static long fact(int n) {
long res = 1;
for (int i = 2; i <= n; i++) { res *= i; }
return res;
}
// A function to find rank of a string in all permutations
// of characters
static long findRank(String s) {
int n = s.length();
long mul = fact(n);
long rank = 1;
// Using a vector of size 26 for lowercase letters
int[] count = new int[26];
// Populate the count array for each character in string
for (int i = 0; i < n; ++i) {
++count[s.charAt(i) - 'a'];
}
// Convert count to cumulative sum
for (int i = 1; i < 26; ++i) {
count[i] += count[i - 1];
}
for (int i = 0; i < n; ++i) {
mul /= (n - i);
// Get index of current character in count array
int charIndex = s.charAt(i) - 'a';
// Add count of characters smaller than current character
if (charIndex > 0) {
rank += count[charIndex - 1] * mul;
}
// Update count array
for (int j = charIndex; j < 26; ++j) {
--count[j];
}
}
return rank;
}
public static void main(String[] args) {
String s = "string";
System.out.println(findRank(s));
}
}
# Factorial
def fact(n):
res = 1
for i in range(2, n + 1):
res *= i
return res
# A function to find rank of a string in all permutations
# of characters
def findRank(s):
n = len(s)
mul = fact(n)
rank = 1
# Using a vector of size 26 for lowercase letters
count = [0] * 26
# Populate the count array for each character in string
for i in range(n):
count[ord(s[i]) - ord('a')] += 1
# Convert count to cumulative sum
for i in range(1, 26):
count[i] += count[i - 1]
for i in range(n):
mul //= (n - i)
# Get index of current character in count array
charIndex = ord(s[i]) - ord('a')
# Add count of characters smaller than current character
if charIndex > 0:
rank += count[charIndex - 1] * mul
# Update count array
for j in range(charIndex, 26):
count[j] -= 1
return rank
if __name__ == "__main__":
s = "string"
print(findRank(s))
using System;
class GfG {
// Factorial
static long fact(int n) {
long res = 1;
for (int i = 2; i <= n; i++) { res *= i; }
return res;
}
// A function to find rank of a string in all permutations
// of characters
static long findRank(string s) {
int n = s.Length;
long mul = fact(n);
long rank = 1;
// Using a vector of size 26 for lowercase letters
int[] count = new int[26];
// Populate the count array for each character in string
for (int i = 0; i < n; ++i) {
++count[s[i] - 'a'];
}
// Convert count to cumulative sum
for (int i = 1; i < 26; ++i) {
count[i] += count[i - 1];
}
for (int i = 0; i < n; ++i) {
mul /= (n - i);
// Get index of current character in count array
int charIndex = s[i] - 'a';
// Add count of characters smaller than current character
if (charIndex > 0) {
rank += count[charIndex - 1] * mul;
}
// Update count array
for (int j = charIndex; j < 26; ++j) {
--count[j];
}
}
return rank;
}
static void Main(string[] args) {
string s = "string";
Console.WriteLine(findRank(s));
}
}
// Factorial
function fact(n) {
let res = 1;
for (let i = 2; i <= n; i++) { res *= i; }
return res;
}
// A function to find rank of a string in all permutations
// of characters
function findRank(s) {
let n = s.length;
let mul = fact(n);
let rank = 1;
// Using a vector of size 26 for lowercase letters
let count = new Array(26).fill(0);
// Populate the count array for each character in string
for (let i = 0; i < n; ++i) {
++count[s.charCodeAt(i) - 'a'.charCodeAt(0)];
}
// Convert count to cumulative sum
for (let i = 1; i < 26; ++i) {
count[i] += count[i - 1];
}
for (let i = 0; i < n; ++i) {
mul = Math.floor(mul / (n - i));
// Get index of current character in count array
let charIndex = s.charCodeAt(i) - 'a'.charCodeAt(0);
// Add count of characters smaller than current character
if (charIndex > 0) {
rank += count[charIndex - 1] * mul;
}
// Update count array
for (let j = charIndex; j < 26; ++j) {
--count[j];
}
}
return rank;
}
// Driver Code
let s = "string";
console.log(findRank(s));
Output
598
Note: The above programs don't work for duplicate characters. To make them work for duplicate characters, find all the characters that are smaller (include equal this time also), do the same as above but, this time divide the rank so formed by p! where p is the count of occurrences of the repeating character.Â