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.

Example:

Input: "aab"
Output: 1
Explanation: The palindrome partitioning ["aa","b"] could be produced using 1 cut.

这道题的意思是:给你一个字符串,至少需要切几下,使得切出来的子字符串都是回文串。只有一个字符的时候,它本身一个回文串,不需要切,切的次数为0。首先,我们要考虑某一段子字符串是不是回文串,然后再考虑切的问题。为了让切的次数最少,也就是说我们需要使得切出来的回文字符串尽量长。我们考虑用一个数组保存子字符串是否为回文,这里可以用动态规划的思想来得到这个数组。我们用dp[i][j]来表示字符串坐标i到j是否为回文。我们可以很轻易的得到如下递推公式:

$ dp[i][j] = max \left\{ \begin{aligned} & 1, i==j \\ & 1, i==j-1 \ and \ s[i]==s[j] \\ & 1, i < j-1 \ and \ dp[i+1][j-1]==1 \ and \ s[i] == s[j] \\ & 0, otherwise \end{aligned} \right. $

当i==j时,只有一个字符,是回文。当i==j-1,即两个字符相邻,判断两个字符是否相等,相等则为回文。当 i < j-1,就是说长度大于等于3,需要先判断dp[i+1][j-1]是不是回文,如果是回文,再判断s[i] == s[j]是否成立,成立的话就是回文。

 得到dp矩阵后,如何去切也是个动态规划问题,定义数组cut[m], m = [0,1..., len(s)], cut[m]表示从坐标m到坐标len(s)需要切多少下。假如我们已经得到从坐标j到末尾需要切多少下的数据cut[j],并且dp[i][j]是回文,则从坐标i到末尾需要cut[i] = min(cut[j]+1, cut[i]),j = [i+1,...,len(s),len(s)+1]。也就是说得到cut[j]后,如果dp[i][j]是回文,那么cut[i]等于cut[j]+1(在cut[j]的基础上再砍一刀)。这个过程需要花费o($n^2$),我们可以在求解dp[i][j]的过程中去求解cut[i]。

$cut[i] = min(cut[j]+1, cut[i]), j = [i+1,...,len(s),len(s)+1]$

我们知道,如果只有一个元素,cut[len(s)-1]=0,那么cut[len(s)]=-1,这也是为什么我们从后面往前推的原因。下面是代码:

 1 class Solution:
 2     def minCut(self, s: str) -> int:
 3         #需要使用dp保存子串是否为回文
 4         #还需要数组保存cut保存到某一位置需要cut多少下
 5         if not s:
 6             return 0
 7         length = len(s)
 8         if length <= 1:
 9             return 0
10         dp = [[0] * length for _ in range(length)]
11         #这里cut千万不能用0来赋值
12         cut = [length] * (length+1)
13         #从后往前推
14         cut[length] = -1
15         for i in range(length-1, -1, -1):
16             for j in range(i, length):
17                 #相同位置
18                 if i == j:
19                     dp[i][j] = 1
20                 #相邻位置
21                 elif i == j-1 and s[i] == s[j]:
22                     dp[i][j] = 1
23                 else:
24                 #相隔的位置,需要判断中间段是否为回文(两个字符相等也要咯)
25                     if dp[i+1][j-1] == 1 and s[i] == s[j]:
26                         dp[i][j] = 1
27                 #如果i-j是回文,那么可以试试切一下看次数会不会比原来小
28                 #cut[i]表示位置i到末尾需要切几下,如果i-j是回文,那么cut[i] = cut[j+1]+1,还要使得cut[i]的值最小
29                 #当只有一个元素时,cut[i]为0
30                 if dp[i][j] == 1:
31                     cut[i] = min(cut[j+1]+1, cut[i])
32         return cut[0]
posted @ 2019-06-10 10:36  搞钱的阿军  阅读(221)  评论(0编辑  收藏  举报