Friday, August 1, 2014

Kth Permutation

Given a string or collection of elements, find the kth permutation of them. For ex, if "123" is the given string then following are their permutations in order: "123", "132", "213", "231", "312", "321".
 
One obvious way to solve the problem is by enumerating all the permutations and eventually stopping when we get to the requested permutation. The approach is brute force as in the worst case it takes up-to O(n!) - for ex, if the requested permutation is n!th one or >n!.
 
Problem can be solved if we skip enumerating all permutations, and get to the required one by using factorials. Below is the corresponding code for the same. I have also posted the brute force one for convenience.

Optimal Approach (O(N))

Code Snippet
  1. public string GetKthPermutation(string s, int k)
  2.         {
  3.             s.ThrowIfNullOrWhiteSpace("a");
  4.             k.Throw("k", ik => ik <= 0);
  5.             int[] factorial = new int[s.Length+1];
  6.             factorial[0] = 0;
  7.             factorial[1] = 1;
  8.             for(int i =2;i<=s.Length; i++)
  9.             {
  10.                 factorial[i] = factorial[i - 1] * i;
  11.             }
  12.             if(k > factorial[s.Length])
  13.             {
  14.                 throw new Exception(string.Format("There will be no '{0}' permuation as the total number of permutations that can be abieved are '{1}'", k, s.Length));
  15.             }
  16.             string kthPermutation = this.GetKthPermutation(s, k, factorial);
  17.             return kthPermutation;
  18.         }
  19.         public string GetKthPermutation(string s, int k, int[] factorial)
  20.         {
  21.             if (k == 1)
  22.             {
  23.                 return s;
  24.             }
  25.             Assert.IsTrue(k > 1);
  26.             int numbeOfPermutationsWithRemainingElements = factorial[s.Length-1];
  27.             string permutation = null;
  28.             if (k <= numbeOfPermutationsWithRemainingElements)
  29.             {
  30.                 //just find the kthPermutation using remaining elements
  31.                 permutation = s[0] + this.GetKthPermutation(s.Substring(1), k,
  32.                     factorial);
  33.                 return permutation;
  34.             }
  35.             int quotient = k / numbeOfPermutationsWithRemainingElements;
  36.             int remainder = k % numbeOfPermutationsWithRemainingElements;
  37.             Assert.IsTrue(quotient > 0);
  38.             int indexOfCurrentCharacter = quotient;
  39.             if(remainder == 0)
  40.             {
  41.                 indexOfCurrentCharacter -= 1;
  42.             }
  43.             permutation = s[indexOfCurrentCharacter].ToString();
  44.             Assert.IsTrue(indexOfCurrentCharacter > 0);
  45.             Assert.IsTrue(indexOfCurrentCharacter < s.Length);
  46.             string remainingCharactersString = s.Substring(0, indexOfCurrentCharacter);
  47.             if(indexOfCurrentCharacter < (s.Length-1))
  48.             {
  49.                 remainingCharactersString += s.Substring(indexOfCurrentCharacter + 1);
  50.             }
  51.             if(remainder == 0)
  52.             {
  53.                 k = numbeOfPermutationsWithRemainingElements;
  54.             }
  55.             else
  56.             {
  57.                 k = remainder;
  58.             }
  59.             permutation += this.GetKthPermutation(remainingCharactersString, k, factorial);
  60.             return permutation;
  61.         }

Brute Force

Code Snippet
  1. public string GetKthPermutation_BruteForce(string s, int k)
  2.         {
  3.             s.ThrowIfNullOrWhiteSpace("a");
  4.             k.Throw("k", ik => ik <= 0);
  5.             int count = 0;
  6.             var permutation = this.GetKthPermutation_BruteForce(s, "", k, ref count);
  7.             if(!string.IsNullOrEmpty(permutation))
  8.             {
  9.                 Assert.AreEqual(count, k);
  10.             }
  11.             return permutation;
  12.         }
  13.         private string GetKthPermutation_BruteForce(string input, string prefixOfPermutation, int k,
  14.             ref int count)
  15.         {
  16.             if (input.Length == 0)
  17.             {
  18.                 count++;
  19.                 if(count == k)
  20.                 {
  21.                     return prefixOfPermutation;
  22.                 }
  23.                 return null;
  24.             }
  25.             for (int i = 0; i < input.Length; i++)
  26.             {
  27.                 string recursiveCallInput = "";
  28.                 if (i != 0)
  29.                 {
  30.                     recursiveCallInput = input.Substring(0, i);
  31.                 }
  32.                 if (i < input.Length - 1)
  33.                 {
  34.                     recursiveCallInput += input.Substring(i + 1);
  35.                 }
  36.                 string prefix = prefixOfPermutation + input[i];
  37.                 var permutation = this.GetKthPermutation_BruteForce(recursiveCallInput, prefix, k, ref count);
  38.                 if(null != permutation)
  39.                 {
  40.                     return permutation;
  41.                 }
  42.             }
  43.             return null;
  44.         }

Unit Tests

Code Snippet
  1. [TestCategory(Constants.PermutationsAndCombinations)]
  2.         [TestMethod]
  3.         public void KthPermutationTests()
  4.         {
  5.             string s1 = "abc";
  6.             string[] perms1 = { "abc", "acb", "bac", "bca", "cab", "cba"};
  7.             for(int i =1;i<=6;i++)
  8.             {
  9.                 string s = this.GetKthPermutation(s1, i);
  10.                 Assert.AreEqual(s, perms1[i - 1]);
  11.                 string ss = this.GetKthPermutation_BruteForce(s1, i);
  12.                 Assert.AreEqual(ss, s);
  13.             }
  14.             s1 = "123";
  15.             perms1 = new string[] {"123", "132", "213", "231", "312", "321"};
  16.             for (int i = 1; i <= 6; i++)
  17.             {
  18.                 string s = this.GetKthPermutation(s1, i);
  19.                 Assert.AreEqual(s, perms1[i - 1]);
  20.                 string ss = this.GetKthPermutation_BruteForce(s1, i);
  21.                 Assert.AreEqual(ss, s);
  22.             }
  23.             s1 = "1234";
  24.             perms1 = new string[] { "1234", "1243", "1324", "1342", "1423", "1432",
  25.                                     "2134", "2143", "2314", "2341", "2413", "2431",
  26.                                     "3124", "3142", "3214", "3241", "3412", "3421",
  27.                                     "4123", "4132", "4213", "4231", "4312", "4321"};
  28.             for (int i = 1; i <= 24; i++)
  29.             {
  30.                 string s = this.GetKthPermutation(s1, i);
  31.                 Assert.AreEqual(s, perms1[i - 1]);
  32.                 string ss = this.GetKthPermutation_BruteForce(s1, i);
  33.                 Assert.AreEqual(ss, s);
  34.             }
  35.         }
 
 

No comments:

Post a Comment