Skip to Content

Palindrome Partitioning II

Home | Coding Interviews | Dynamic Programming | Palindrome Partitioning II

Given a string s, partition s such that every substring of the partition is a palindrome.

Return the minimum cuts needed for a palindrome partitioning of s.

# top-down memo
class Solution:
    def minCut(self, s: str) -> int:
        memo_mincut = dict()
        memo_palindrome = dict()
        
        def is_palindrome(start, end):
            if (start, end) in memo_palindrome:
                return memo_palindrome[(start, end)]
            
            if start >= end:
                return True
            
            memo_palindrome[(start, end)] = (is_palindrome(start + 1, end - 1) and s[start] == s[end])
            return memo_palindrome[(start, end)]
        
        def dfs(idx):
            if is_palindrome(idx, len(s) - 1):
                return 0
            
            if idx in memo_mincut:
                return memo_mincut[idx]
            
            ans = math.inf
            for i in range(idx, len(s)):
                if is_palindrome(idx, i):
                    ans = min(ans, dfs(i + 1) + 1)
            
            memo_mincut[idx] = ans
            return ans

        return dfs(0)

# bottom-up DP
class Solution:
    def minCut(self, s: str) -> int:
        N = len(s)
        dp = [math.inf] * (N + 1)
        dp[0] = -1

        memo_palindrome = dict()

        def is_palindrome(start, end):
            if (start, end) in memo_palindrome:
                return memo_palindrome[(start, end)]

            if start >= end:
                return True
            
            memo_palindrome[(start, end)] = is_palindrome(start + 1, end - 1) and s[start - 1] == s[end - 1]
            return memo_palindrome[(start, end)]
        
        for i in range(1, N + 1):
            for j in range(i, 0, -1):
                if is_palindrome(j, i):
                    dp[i] = min(dp[i], dp[j - 1] + 1)
        
        return dp[N]

# two-pointer optimised (i.e. expand from centre)
class Solution:
    def minCut(self, s: str) -> int:
        N = len(s)
        dp = [math.inf] * N

        def optimal_sub_problem(left, right, dp):
            while 0 <= left  and  right < len(s) and s[left] == s[right]:
                if left == 0:
                    dp[right] = 0

                dp[right] = min(dp[right], dp[left - 1] + 1)
                left -= 1
                right += 1
        
        for i in range(N):
            optimal_sub_problem(i, i, dp)
            optimal_sub_problem(i - 1, i, dp)

        return dp[N - 1]

Posted by Jamie Meyer 13 days ago