Given a string s
, partition s
such that every substring of the partition is a palindrome. Return all possible palindrome partitioning of s
.
Example 1: Input: s = "aab" Output: [["a","a","b"],["aa","b"]]
Example 2: Input: s = "a" Output: [["a"]]
Constraints: 1 <= s.length <= 16 s contains only lowercase English letters.
class Solution:
def partition(self, s: str) -> list[list[str]]:
res = []
n = len(s)
def is_palindrome(sub):
return sub == sub[::-1]
def backtrack(start, path):
if start == n:
res.append(path[:]) # Append a copy of the current path
return
for i in range(start, n):
sub = s[start:i + 1]
if is_palindrome(sub):
path.append(sub)
backtrack(i + 1, path)
path.pop() # Backtrack: remove the last added substring
backtrack(0, [])
return res
A naive approach would involve generating all possible partitions of the string s
and then checking if each partition consists of palindromic substrings. This method is highly inefficient because it considers many invalid partitions.
The optimal approach uses backtracking. It builds partitions recursively, checking at each step if the current substring is a palindrome. If it is, the substring is added to the current partition, and the algorithm recurses on the remaining part of the string. This avoids generating and checking invalid partitions.
For s = "aab"
:
[]
.i = 0
: sub = "a"
is a palindrome. Add it to the path: ["a"]
. Recurse with start = 1
.i = 1
: sub = "a"
is a palindrome. Add it to the path: ["a", "a"]
. Recurse with start = 2
.i = 2
: sub = "b"
is a palindrome. Add it to the path: ["a", "a", "b"]
. Recurse with start = 3
. start == n
. Append the current path to res
: res = [["a", "a", "b"]]
."b"
from the path: ["a", "a"]
."a"
from the path: ["a"]
.i = 1
: sub = "aa"
is a palindrome. Add it to the path: ["a", "aa"]
. Recurse with start = 2
.i = 2
: sub = "b"
is a palindrome. Add it to the path: ["a", "aa", "b"]
. Recurse with start = 3
. start == n
. Append the current path to res
: res = [["a", "a", "b"], [["a", "aa", "b"]]]
."b"
from the path: ["a", "aa"]
."aa"
from the path: ["a"]
."a"
from the path: []
.i = 1
: sub = "aab"
is not a palindrome.res
.s
. In the worst case, the number of possible partitions can be exponential, specifically O(2n-1), where n is the length of the string.s
.s
.res
list. In the worst-case scenario, where each character is a palindrome, the number of partitions can be exponential, specifically O(2n-1).n
, and we store copies of the substrings. Therefore, the space required to store the result is also approximately O(N * 2^N), where N is the length of the input string s
.s
.s
is empty, the function should return an empty list [[]]
. This is because an empty string is considered a palindrome.s
contains only one character, the function should return a list containing a list with the single character, e.g., ["a"]
for s = "a"
.s
has no palindromic partitions (e.g., s = "abc"
), the function should return a list of lists containing individual characters. For example, ["a", "b", "c"]
.1 <= s.length <= 16
mitigates this.s = "aaa"
, the output should be [["a", "a", "a"], ["a", "aa"], ["aa", "a"], ["aaa"]]
.