The shortest palindrome problem involves converting a given string into the shortest possible palindrome by adding characters to its beginning.
The shortest palindrome problem in Python
Key takeaways:
The goal is to convert the given string
sinto the shortest palindrome by adding characters only to the beginning of the string.Brute force adds the necessary characters in reverse to form the shortest palindrome.
The Knuth-Morris-Pratt (KMP) algorithm enables finding the longest palindromic prefix in linear time
. Concatenating the string with its reverse and a delimiter in KMP approach helps identify the palindrome efficiently.
The KMP prefix array reveals the longest palindromic prefix in the string.
The shortest palindrome problem is a popular coding challenge often discussed in technical interviews. The task is to transform a given string, s, into its shortest palindrome by adding characters to the beginning.
Problem statement
Given a string s, convert it into the shortest palindrome by adding characters at the start.
Example:
Input: aacecaaa
Output: aaacecaaa
Approach 1: Brute force
The brute force approach solves the problem by identifying the longest palindromic prefix in the string and then adding the missing characters to the start to make it a palindrome.
Let’s walk through the steps of the brute force solution:
Start by reversing the input string
sto obtainrev_s.
Example:
Ifs = "aacecaaa", thenrev_s = "aaacecaa".The next step is to check for the longest palindromic prefix in
rev_sthat matches the beginning ofs. This is done by iterating through the characters ofsandrev_sand checking which prefix forms a palindrome.
Example:
Fors = "aacecaaa", the longest palindromic prefix inrev_s = "aaacecaa"is"aacecaa".After identifying the longest palindromic prefix, we add the characters from
rev_sthat are not part of the palindrome to the beginning ofs.
Example:
The non-palindromic portion ofrev_sis"a". Add this to the start ofsto get the result:
Result:"aaacecaaa".
Let’s look at the following illustration to get a better understanding of the approach:
Let’s look at the code for the solution we just discussed.
def shortest_palindrome_bruteforce(s):n = len(s)s_rev = s[::-1]for i in range(n, 0, -1):if s[:i] == s_rev[-i:]:return s_rev[:n-i] + sreturn "" # If s has no palindrome substring at all.test_cases = ["aacecaaa", # Expected: "aaacecaaa""abcd", # Expected: "dcbabcd""racecar", # Expected: "racecar""a" # Expected: "a"]for test in test_cases:print(f"Input: {test} -> Shortest Palindrome: {shortest_palindrome_bruteforce(test)}")
Time and space complexity
The time complexity of this approach is
, where is the length of the input string. This is because, in the worst case, the function checks each prefix and verifies if it’s a palindrome by reversing it. The space complexity is linear
as it uses additional space to store the reversed string version.
Note: This approach works but isn’t the most efficient in terms of time and space complexity.
Approach 2: Knuth-Morris-Pratt (KMP) algorithm
The Knuth-Morris-Pratt (KMP) algorithm is famous for pattern searching. The approach is much more efficient than the brute force method and works in linear time. But how does it relate to our problem?
Imagine concatenating the string s and its reverse with a delimiter: s + "#" + rev_s. The problem now is to find the palindrome substring which spans the entire length of s. Using the KMP algorithm, we can compute a prefix array of this concatenated string, which will help us find the desired palindrome substring.
Let’s walk through the steps of this approach:
The first step is to concatenate the original string
swith a special delimiter (e.g.,#) and its reversed versions[::-1]. This ensures that we can easily compute the longest palindromic prefix. The delimiter ensures no overlap between the original string and its reverse when calculating the prefix array. For example: Ifs = "aacecaaa", the concatenated string will be:"aacecaaa#aaacecaa"Next, we compute the prefix array for the concatenated string using the KMP algorithm. The
prefixarray stores the lengths of the longest proper prefix that is also a suffix for each substring in the concatenated string.We initialize the
prefixarray with zeros, and use two pointersi(which iterates over the concatenated string) andj(which tracks the length of the longest matching prefix).If characters at positions
iandjmatch, we extend the match by incrementingj. Otherwise, we use the previously computed prefix values to find a shorter matching prefix and adjustj.
The last value in the prefix array (
prefix[-1]) gives the length of the longest palindromic prefix in the original strings.To form the shortest palindrome, we need to add the non-palindromic portion of the original string in reverse at the beginning of
s. This is done by slicings[palindrome_length:](the part that is not part of the palindrome) and reversing it, then concatenating it with the original strings.Finally, the result is the shortest palindrome formed by adding the required characters to the front of
s.
Let’s look at the code for the solution we just discussed.
def shortest_palindrome(s):concatenated = s + "#" + s[::-1]def get_prefix_array(concatenated):prefix = [0] * len(concatenated)j = 0for i in range(1, len(concatenated)):while j > 0 and concatenated[i] != concatenated[j]:j = prefix[j - 1]if concatenated[i] == concatenated[j]:j += 1prefix[i] = jreturn prefixpalindrome_length = get_prefix_array(concatenated)[-1]result = s[palindrome_length:][::-1] + sreturn resulttest_cases = ["aacecaaa", # Expected: "aaacecaaa""abcd", # Expected: "dcbabcd""racecar", # Expected: "racecar""a" # Expected: "a"]for test in test_cases:print(f"Input: {test} -> Shortest Palindrome: {shortest_palindrome(test)}")
Time and space complexity
The time complexity of this approach is linear
, where is the length of the input string s. This is because the KMP algorithm computes the prefix array in linear time, and the subsequent operations are linear as well.The space complexity is also linear
, as space is used to store the concatenated string and the prefix array.
You can explore Educative’s "Grokking the Coding Interview Patterns" and "Grokking Dynamic Programming Interview" courses, which include optimized strategies for problems like the shortest palindrome and many more.
Conclusion
The KMP algorithm approach is efficient and works in linear time compared to the brute force method, which has quadratic time complexity. By leveraging string-matching techniques, this solution optimally computes the shortest palindrome.
Frequently asked questions
Haven’t found what you were looking for? Contact Us
What is the shortest palindrome problem?
What is the brute force approach to solving the shortest palindrome problem?
How does the KMP algorithm improve efficiency?
What is the concatenation strategy in the KMP approach?
Free Resources