Solution: Palindrome Permutation

Let's solve the Palindrome Permutation problem using the Knowing What to Track pattern.

Statement#

For a given string, st, find whether or not a permutation of this string is a palindrome. You should return TRUE if such a permutation is possible and FALSE if it isn’t possible.

Constraints:

  • 11 \leq st.length 1000\leq 1000
  • The string will contain lowercase English letters.

Solution#

So far, you’ve probably brainstormed some approaches and have an idea of how to solve this problem. Let’s explore some of these approaches and figure out which one to follow based on considerations such as time complexity and any implementation constraints.

Naive approach#

The naive solution is to first compute all possible permutations of a given string and then iterate over each permutation to see if it’s a palindrome. We can use two pointers to traverse the computed permutation from the beginning and the end simultaneously, comparing each character at every step. If the two pointers point to the same character until they cross each other, the string is a palindrome.

The number of possible permutations for a string of length nn is n!n!, and it requires iterating through the nn characters to construct each permutation. Therefore, it will take O(n!×n)O(n! \times n) time to generate all these permutations. Then, for each permutation, we need to check whether it is a palindrome. Checking this condition requires iterating through the permutation once, which takes O(n)O(n) time. Therefore, the overall time complexity of this naive approach is O((n!×n)×n)O((n! \times n)\times n). The space complexity is O(n!)O(n!) because we need to store all the permutations.

Optimized approach using frequency counting#

This is the stereotypical example of a problem whose naive solution is very inefficient, yet by identifying a few key properties of the problem, we are able to devise a solution with superior time and space complexity measures. In this case, there are three key observations:

  • In a palindrome of even length, all the characters occur an even number of times. For example, in the palindrome “aaaabbaaaa”, “a” occurs four times and “b” occurs twice.
  • In a palindrome of odd length, the character in the middle of the string appears once, and all the others occur an even number of times. For example, in the palindrome “aaabaaa”, “a” occurs six times and “b” occurs once.
  • The observations above are true for all the permutations of a palindrome. For example, “aaaaaaaabb” is a permutation of “aaaabbaaaa” in which “a” still occurs four times and “b” occurs twice.

So, to decide whether or not a given string has a permutation that is a palindrome, all we need to do is count the number of times each character appears in it and then check how many characters appear an odd number of times.

  • If no characters appear an odd number of times, then the string is of even length and has a permutation that is a palindrome.
  • If only one character appears an odd number of times, then the string is of odd length and has a permutation that is a palindrome.
  • If more than one character appears an odd number of times, then the string does not have a permutation that is a palindrome.

The slides below illustrate the working of our proposed solution:

Created with Fabric.js 3.6.6 b a e f e a b Key Value Populating the map st = map

1 of 13

Created with Fabric.js 3.6.6 b a e f e a b Populating the map Key b Value 1 st = map

2 of 13

Created with Fabric.js 3.6.6 b a e f e a b Populating the map Key b a Value 1 1 st = map

3 of 13

Created with Fabric.js 3.6.6 b a e f e a b Populating the map Key b a e Value 1 1 1 st = map

4 of 13

Created with Fabric.js 3.6.6 b a e f e a b Key b a e f Value 1 1 1 1 Populating the map st = map

5 of 13

Created with Fabric.js 3.6.6 Populating the map b a e f e a b Key b a e f Value 1 1 2 1 st = map

6 of 13

Created with Fabric.js 3.6.6 b a e f e a b Key b a e f Value 1 2 2 1 Populating the map st = map

7 of 13

Created with Fabric.js 3.6.6 Populating the map Key b a e f Value 2 2 2 1 b a e f e a b st = map

8 of 13

Created with Fabric.js 3.6.6 count of odd-frequency characters = 2%2 = 0 Key b a e f Value 2 2 2 1 b a e f e a b Populating the map st = map

9 of 13

Created with Fabric.js 3.6.6 Populating the map Key b a e f Value 2 2 2 1 count of odd-frequency characters = 0 + 2%2 = 0 b a e f e a b st = map

10 of 13

Created with Fabric.js 3.6.6 Key b a e f Value 2 2 2 1 b a e f e a b Populating the map count of odd-frequency characters = 0 + 2%2 = 0 st = map

11 of 13

Created with Fabric.js 3.6.6 b a e f e a b Populating the map count of odd-frequency characters = 0 + 1%2 = 1 Key b a e f Value 2 2 2 1 st = map

12 of 13

Created with Fabric.js 3.6.6 Populating the map Key b a e f Value 2 2 2 1 b a e f e a b st = map count of odd-frequency characters = 1Palindromic permutation is possible, so we will return TRUE.

13 of 13

Note: In the following section, we will gradually build the solution. Alternatively, you can skip straight to just the code.

Step-by-step solution construction#

Building on the above idea, the first step in our algorithm is to traverse the input string and populate a hash map with characters and their frequencies. If a character is already present, we’ll increment its frequency by 11. Otherwise, we set its frequency =1= 1.

Populating the hash map

Next, we traverse the hash map and count the characters with an odd number of occurrences. We keep track of this number using a variable count that is incremented every time a character with an odd frequency is encountered.

Counting the numbers

Lastly, we check if count is greater than 11. If yes, no permutation is a palindrome. Otherwise, at least one of the permutations of the given string is a palindrome.

Palindrome permutation

Just the code#

Here’s the complete solution to this problem:

Palindrome permutation

Solution summary#

To recap, the solution to this problem can be divided into the following parts:

  1. Populate a hash map with string characters and their frequencies.
  2. Count the characters with frequency 1\geq 1.
  3. If count >1> 1, return FALSE. Otherwise, return TRUE.

Time complexity#

There are two parts in this solution. In the first part, we iterate over a string of nn characters, checking their presence in a hash map and adding/updating the hash map as we go. The average time complexity for looking up values in a hash map is O(1)O(1). The worst case time complexity for looking up values in a hash map is O(k)O(k), where kk is the number of elements in the hash map. Since the string only contains lowercase English letters, there are 2626 distinct characters in this case.

Thus, the average time complexity of the first part of the solution is O(n)O(n) while the worst case time complexity is O(n×k)O(n \times k). In this scenario, the worst case time complexity is O(26×n)O(26 \times n), which simplifies to O(n)O(n).

In the second part, our loop iterates at most 2626 times. Hence the time complexity of the second part is O(1)O(1).

In summary, our solution’s average and worst case time complexity is O(n)O(n).

Space complexity#

The hash map can grow up to nn if all the characters in the string are distinct. However, the number of distinct characters is bounded and so is the space complexity. So, the space complexity is O(1)O(1).

Palindrome Permutation

Valid Anagram