给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回符合要求的最少分割次数。
示例:
输入: "aab"
输出: 1
解释: 进行一次分割就可将 s 分割成 ["aa","b"] 这样两个回文子串。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/palindrome-partitioning-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
第一种麻瓜思路:
把所有的分割可能全部找出来,然后找长度最小的分割可能,这个最小长度 - 1就是答案。
class Solution(object):
def minCut(self, s):
"""
:type s: str
:rtype: int
"""
res = self.partition(s)
min_l = 2 ** 32
for item in res:
min_l = min(min_l, len(item))
return min_l - 1
def partition(self, s):
"""
:type s: str
:rtype: List[List[str]]
"""
res = []
def dfs(start, tmp):
if start >= len(s): #结束条件
res.append(tmp[:])
return
for i in range(start, len(s)):
if s[start:i + 1] == s[start:i + 1][::-1]: #找到一个回文子串
tmp.append(s[start:i + 1])
dfs(i + 1, tmp ) #进行下一层搜索
tmp.pop() #回溯
dfs(0, [])
return res
第二种思路:
动态规划。
状态转移方程:
dp[i] = min(dp[i], dp[j] + 1) if dp[j : i] == dp[j : i][::-1]
class Solution(object):
def minCut(self, s):
"""
:type s: str
:rtype: int
"""
if s == s[::-1]:
return 0
for i in range(1, len(s) + 1):
if s[:i] == s[:i][::-1] and s[i:] == s[i:][::-1]:
return 1
#以上是进行预处理,因为testcase大多数答案都是0/1,所以可以加快速度
dp = [i - 1 for i in range(len(s) + 1)]
for i in range(1, len(s) + 1):
for j in range(i):
if s[j:i] == s[j:i][::-1]:
dp[i] = min(dp[i], dp[j] + 1)
return dp[-1]
第三种思路:
动态规划 + 中心拓展法。
找以每个字符为中心的长度最长的回文子串。
class Solution(object):
def minCut(self, s):
"""
:type s: str
:rtype: int
"""
if s == s[::-1]:
return 0
for i in range(1, len(s) + 1):
if s[:i] == s[:i][::-1] and s[i:] == s[i:][::-1]:
return 1
dp = [len(s) for i in range(len(s))]
for i in range(0, len(s)):
self.centeralExtend(s, i, i, dp)
self.centeralExtend(s, i, i+1, dp)
# print dp
return dp[-1]
def centeralExtend(self, string, left, right, dp):
while left >= 0 and right < len(string) and string[left] == string[right]:
if left > 0:
dp[right] = min(dp[right], dp[left - 1] + 1)
else:
dp[right] = 0
left -= 1
right += 1