132. 分割回文串 II

Q:

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。

返回符合要求的最少分割次数。

示例:

输入: “aab”
输出: 1
解释: 进行一次分割就可将 s 分割成 [“aa”,“b”] 这样两个回文子串。

A:

1.我最开始想到了要两次DP,先算一个是否是回文数的dp数组,再算所求的DP。
但第二个DP数组我用的二维数组,然后就变成了O(N^3)时间。因为对于其中每个元素都要从左边界遍历到右边界进行分割,没有想到可以利用第一个DP中的数据。
代码是对的,自己机器跑了,但AC不了,第26个用例超时:

class Solution:
    def minCut(self, s: str) -> int:
        #先算一个dp数组记录是否是回文数
        #dp[i][j]记录s[i,j]闭区间是否是回文数
        l=len(s)
        if not s:
            return 0
        dp=[[0 for i in range(l)] for j in range(l)]
        for i in range(l):
            dp[i][i]=1  #一个元素的一定是回文数,递推起始条件
        for i in range(l-1):
            if s[i]==s[i+1]:
                dp[i][i+1]=1    #两个相邻元素的若值相同,也为回文数
        for step in range(2,l):
            i=0
            while i+step<l:
                j=i+step    #j为右边界,考察s[i,j]是否是回文数
                #矩阵的右上半部,满足i<=j才有意义
                dp[i][j]=int(dp[i+1][j-1] and s[i]==s[j])
                #当前字符串为回文数的条件:两边界截掉是回文数
                i+=1
        #回文数dp数组建立好,考虑对于s[i,j]字符串的最少分割次数f(i,j),
        #若当前字符串为回文数即dp[i][j]==1则分割次数f(i,j)为0,不用割,
        #否则可能的切割位置为i+1到j-1,假设切割位置为k(i<k<j),
        #还需要知道f(i,k),f(k+1,j),故还需要一个求最少分割次数的dp数组
        dp2=[[float('inf') for i in range(l)] for j in range(l)]
        for i in range(l):
            dp2[i][i]=0     #单个字符串不用割,其他元素初始化为无穷大
        for step in range(1,l):  #对[i,i+step]闭区间考察,i取0时,0+step应该小于l
            i=0
            while i+step<l:
                if dp[i][i+step]==1:    #若s[i,i+step]已是回文
                    dp2[i][i+step]=0
                else:
                    #若s[i,i+step]不是回文,则一定要切,下面开始切
                    cur_min=float('inf')
                    for cut in range(i,i+step):
                        #找割数最小的割法
                        cur_min=min(cur_min,dp2[i][cut]+dp2[cut+1][i+step]+1)
                    dp2[i][i+step]=cur_min
                i+=1
        # print(dp)
        # print(dp2)
        return dp2[0][l-1]

2.正确代码:
第一个dp数组一样,必须是二维的。第二个dp数组一维的,dp2[i]表示从i开始到末尾的字符串的最小分割数,对于每个dp[i],在i+1到n-1找分割点k就完事了,i到k为回文串通过第一个DP数组查询,O(1) ,剩下的k到n-1是之前算好的,所以第二个DP数组要从右往左算,因为右边界不动。总体O(N^2)复杂度。

class Solution:
    def minCut(self, s: str) -> int:
        l=len(s)    #先算一个dp数组记录是否是回文,dp[i][j]记录s[i,j]闭区间是否是回文数
        if not l:
            return 0
        dp=[[0 for i in range(l)] for j in range(l)]
        for i in range(l-1,-1,-1):  #从下往上,从右往左算
            for j in range(l-1,i-1,-1):
                if s[i]==s[j]:
                    dp[i][j]=1 if j-i<2 else dp[i+1][j-1]
        dp2=[float('inf') for i in range(l)]  #dp2[i]表示从i开始到末尾字符串的最小分割数
        for i in range(l-1,-1,-1):  
            if dp[i][l-1]:  #当前字符串是回文数
                dp2[i]=0
            else:
                _min=float('inf')
                for cut in range(i,l-1):    #不同割点位置
                    if dp[i][cut]==1:
                        _min=min(dp2[cut+1]+1,_min)
                dp2[i]=_min
        print(dp2)
        return dp2[0]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值