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
Constraints:
-
st.length
- 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 is , and it requires iterating through the characters to construct each permutation. Therefore, it will take 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 time. Therefore, the overall time complexity of this naive approach is . The space complexity is 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:
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 . Otherwise, we set its frequency .
class PalindromePermutation {public static String printArrayWithMarkers(String arr, int pvalue) {String out = "";for (int i = 0; i < pvalue; i++) {out += arr.charAt(i);}out += "«" + arr.charAt(pvalue) + "»";for (int i = pvalue + 1; i < arr.length(); i++) {out += arr.charAt(i);}return out;}public static boolean permutePalindrome(String st) {// Create a hashmap to keep track of the characters and their occurrencesSystem.out.println("\n\tPopulating the hashmap");HashMap < Character,Integer > frequencies = new HashMap < Character,Integer > ();int index = 0;for (int i = 0; i < st.length(); i++) {System.out.println("\t\t" + printArrayWithMarkers(st, index));index += 1;System.out.println("\t\tCharacter: " + st.charAt(i));System.out.println("\t\tHashmap: " + frequencies);if (frequencies.containsKey(st.charAt(i))) {System.out.println("\t\t\t" + st.charAt(i) + " is already present in the hashmap");System.out.print("\t\t\tUpdating its frequency ⟶ ");frequencies.put(st.charAt(i), frequencies.get(st.charAt(i)) + 1);System.out.println(frequencies + "\n");}else {System.out.println("\t\t\t" + st.charAt(i) + " is not present in the hashmap");System.out.print("\t\t\tSetting its frequency = 1 ⟶ ");frequencies.put(st.charAt(i), 1);System.out.println(frequencies + "\n");}}return false;}public static void main(String args[]) {List < String > strArray = Arrays.asList("baefeab", "abc", "xzz", "jjadd", "kllk");for (int i = 0; i < strArray.size(); i++) {System.out.println(i + 1 + ".\tInput string: " + strArray.get(i));boolean result = permutePalindrome(strArray.get(i));if (result) System.out.println("\n\tInput string has permutations that are palindromes");else System.out.println("\n\tInput string does not have a permutation that's a palindrome");System.out.println(PrintHyphens.repeat("-", 100));}}}
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.
class PalindromePermutation {public static String printArrayWithMarkers(String arr, int pvalue) {String out = "";for (int i = 0; i < pvalue; i++) {out += arr.charAt(i);}out += "«" + arr.charAt(pvalue) + "»";for (int i = pvalue + 1; i < arr.length(); i++) {out += arr.charAt(i);}return out;}public static boolean permutePalindrome(String st) {// Create a hashmap to keep track of the characters and their occurrencesSystem.out.println("\n\tPopulating the hashmap");HashMap < Character,Integer > frequencies = new HashMap < Character,Integer > ();int index = 0;for (int i = 0; i < st.length(); i++) {System.out.println("\t\t" + printArrayWithMarkers(st, index));index += 1;System.out.println("\t\tCharacter: " + st.charAt(i));System.out.println("\t\tHashmap: " + frequencies);if (frequencies.containsKey(st.charAt(i))) {System.out.println("\t\t\t" + st.charAt(i) + " is already present in the hashmap");System.out.print("\t\t\tUpdating its frequency ⟶ ");frequencies.put(st.charAt(i), frequencies.get(st.charAt(i)) + 1);System.out.println(frequencies + "\n");}else {System.out.println("\t\t\t" + st.charAt(i) + " is not present in the hashmap");System.out.print("\t\t\tSetting its frequency = 1 ⟶ ");frequencies.put(st.charAt(i), 1);System.out.println(frequencies + "\n");}}// Traverse the hashmap and update the count by 1, whenever a// character with odd number of occurences is found.int count = 0;System.out.println("\n\tCounting the characters with odd frequencies");System.out.println("\t\tHash map: " + frequencies);System.out.println("\t\tCount = " + count);for (Character ch: frequencies.keySet()) {System.out.println("\t\tFrequency of '" + ch + "' = " + frequencies.get(ch));if (frequencies.get(ch) % 2 != 0) {System.out.println("\t\t\tIncrementing count: " + count + " + 1 = " + (count + 1));count += 1;}else System.out.println("\t\t\tFrequency is not odd, moving to the next character\n");}return false;}public static void main(String args[]) {List < String > strArray = Arrays.asList("baefeab", "abc", "xzz", "jjadd", "kllk");for (int i = 0; i < strArray.size(); i++) {System.out.println(i + 1 + ".\tInput string: " + strArray.get(i));boolean result = permutePalindrome(strArray.get(i));if (result) System.out.println("\n\tInput string has permutations that are palindromes");else System.out.println("\n\tInput string does not have a permutation that's a palindrome");System.out.println(PrintHyphens.repeat("-", 100));}}}
Lastly, we check if count
is greater than . If yes, no permutation is a palindrome. Otherwise, at least one of the permutations of the given string is a palindrome.
class PalindromePermutation {public static String printArrayWithMarkers(String arr, int pvalue) {String out = "";for (int i = 0; i < pvalue; i++) {out += arr.charAt(i);}out += "«" + arr.charAt(pvalue) + "»";for (int i = pvalue + 1; i < arr.length(); i++) {out += arr.charAt(i);}return out;}public static boolean permutePalindrome(String st) {// Create a hashmap to keep track of the characters and their occurrencesSystem.out.println("\n\tPopulating the hashmap");HashMap < Character,Integer > frequencies = new HashMap < Character,Integer > ();int index = 0;for (int i = 0; i < st.length(); i++) {System.out.println("\t\t" + printArrayWithMarkers(st, index));index += 1;System.out.println("\t\tCharacter: " + st.charAt(i));System.out.println("\t\tHashmap: " + frequencies);if (frequencies.containsKey(st.charAt(i))) {System.out.println("\t\t\t" + st.charAt(i) + " is already present in the hashmap");System.out.print("\t\t\tUpdating its frequency ⟶ ");frequencies.put(st.charAt(i), frequencies.get(st.charAt(i)) + 1);System.out.println(frequencies + "\n");}else {System.out.println("\t\t\t" + st.charAt(i) + " is not present in the hashmap");System.out.print("\t\t\tSetting its frequency = 1 ⟶ ");frequencies.put(st.charAt(i), 1);System.out.println(frequencies + "\n");}}// Traverse the hashmap and update the count by 1, whenever a// character with odd number of occurences is found.int count = 0;System.out.println("\n\tCounting the characters with odd frequencies");System.out.println("\t\tHash map: " + frequencies);System.out.println("\t\tCount = " + count);for (Character ch: frequencies.keySet()) {System.out.println("\t\tFrequency of '" + ch + "' = " + frequencies.get(ch));if (frequencies.get(ch) % 2 != 0) {System.out.println("\t\t\tIncrementing count: " + count + " + 1 = " + (count + 1));count += 1;}else System.out.println("\t\t\tFrequency is not odd, moving to the next character\n");}// If the count is smaller than or equal to 1 then the permutation exists,// otherwise does notSystem.out.println("\n\tCount: " + count);if (count <= 1) {System.out.println("\tCount is <= 1, return TRUE");return true;}else {System.out.println("\tCount > 1, returning FALSE");return false;}}public static void main(String args[]) {List < String > strArray = Arrays.asList("baefeab", "abc", "xzz", "jjadd", "kllk");for (int i = 0; i < strArray.size(); i++) {System.out.println(i + 1 + ".\tInput string: " + strArray.get(i));boolean result = permutePalindrome(strArray.get(i));if (result) System.out.println("\n\tInput string has permutations that are palindromes");else System.out.println("\n\tInput string does not have a permutation that's a palindrome");System.out.println(PrintHyphens.repeat("-", 100));}}}
Just the code
Here’s the complete solution to this problem:
class PalindromePermutation {public static boolean permutePalindrome(String st) {HashMap < Character,Integer > frequencies = new HashMap < Character,Integer > ();for (int i = 0; i < st.length(); i++) {if (frequencies.containsKey(st.charAt(i))) {frequencies.put(st.charAt(i), frequencies.get(st.charAt(i)) + 1);}else {frequencies.put(st.charAt(i), 1);}}int count = 0;for (Character ch: frequencies.keySet()) {if (frequencies.get(ch) % 2 != 0) {count += 1;}}if (count <= 1) {return true;}else {return false;}}public static void main(String args[]) {List < String > strArray = Arrays.asList("baefeab", "abc", "xzz", "jjadd", "kllk");for (int i = 0; i < strArray.size(); i++) {System.out.println(i + 1 + ".\tInput string: " + strArray.get(i));boolean result = permutePalindrome(strArray.get(i));if (result) System.out.println("\n\tInput string has permutations that are palindromes");else System.out.println("\n\tInput string does not have a permutation that's a palindrome");System.out.println(PrintHyphens.repeat("-", 100));}}}
Solution summary
To recap, the solution to this problem can be divided into the following parts:
- Populate a hash map with string characters and their frequencies.
- Count the number of characters with an odd frequency.
- If the count of characters with an odd frequency exceeds , return FALSE. Otherwise, return TRUE.
Time complexity
There are two parts in this solution. In the first part, we iterate over a string of 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 . The worst case time complexity for looking up values in a hash map is , where is the number of elements in the hash map. Since the string only contains lowercase English letters, there are distinct characters in this case.
Thus, the average time complexity of the first part of the solution is while the worst case time complexity is . In this scenario, the worst case time complexity is , which simplifies to .
In the second part, our loop iterates at most times. Hence, the time complexity of the second part is .
In summary, our solution’s average and worst case time complexity is .
Space complexity
The hash map can grow up to 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 .
Level up your interview prep. Join Educative to access 70+ hands-on prep courses.