leetcode 131 回文串预处理(记忆化+DP) + dfs搜索

此题判断子序列是否为回文串很重要,能省很多事情。一种方法就是用dp得出,另一种是记忆化搜索。得出结果后,可以用两个数组和传统dfs来得到所有可能的拆分结果。
dp中,两个点要注意:正常状态要看左下角,即左索引+1,右索引-1的情况,所以需要我们从上往下建表,需要用到先i后j,用j,i进行索引,这个是个很强的技巧。第二个点是找到连续的相同的数,这点的处理也很棒。处理的规则是:只关注连续的两个。因为两个找到后,后续不管是三个四个都可以用左右扩张的方式得到。

class Solution:
    def partition(self, s: str) -> List[List[str]]:
        # 预处理,得到子序列是否为回文串结果
        dp1 = [[0 for _ in s] for _ in s]
        for i in range(len(s)):
            dp1[i][i] = 1
            for j in range(i):
                if s[i] == s[j] and (dp1[j+1][i-1] or i == j+1):
                    dp1[j][i] = 1
        
        # dfs记忆化搜索
        ret, ans = [], []
        def dfs(i):
            if i == len(s): 
                ret.append(ans[:])
                return
            
            for j in range(i, len(s)):
                if dp1[i][j]:
                    ans.append(s[i:j+1])
                    dfs(j+1)
                    ans.pop()

        dfs(0)

        return ret

方法二的记忆化搜索预处理更为强大,由于它和DP是反向的,正常情况不考虑,如果出现i >= j的情况,那一定是剩一个元素或者是两个元素相同的情况,在s[i]和s[j]相同的情况下,是一定满足的。DP不能用是因为他是从内到外,根本不会出现这样的情况。

class Solution:
    def partition(self, s: str) -> List[List[str]]:
        # 记忆化搜索预处理
        @cache
        def dp(i, j):
            if i >= j: return 1
            if s[i] == s[j]: return dp(i+1, j-1)
            else: return 0
        
        # dfs记忆化搜索
        ret, ans = [], []
        def dfs(i):
            if i == len(s): 
                ret.append(ans[:])
                return
            
            for j in range(i, len(s)):
                if dp(i, j):
                    ans.append(s[i:j+1])
                    dfs(j+1)
                    ans.pop()

        dfs(0)

        return ret

其实前两种方法的预处理还是弱了点,咱们可以用马拉车实现O(N)的预处理。

class Solution:
    def partition(self, s: str) -> List[List[str]]:
        # 马拉车处理
        if len(s) < 1: return s
        ss = "#"
        for c in s:
            ss += c + '#'

        maxRight, center = 0, 0
        dp = [0 for _ in ss]

        def expand(i, j, maxRight, center):
            while i+j < len(ss) and ss[i+j] == ss[i-j]:
                j += 1
            if i+j-1 > maxRight:
                maxRight = i+j-1
                center = i
            dp[i] = j-1
            return maxRight, center

        for i, c in enumerate(ss):
            mirror = 2*center - i
            if i >= maxRight or mirror < 0: 
                maxRight, center = expand(i, 1, maxRight, center)
            elif dp[mirror] < maxRight-i:
                dp[i] = dp[mirror]
            elif dp[mirror] > maxRight-i:
                dp[i] = maxRight-i
            else:
                maxRight, center = expand(i, dp[mirror]+1, maxRight, center)

        def isPara(i, j):
            return dp[i+j+1] >= j-i
        
        # dfs回溯
        ret, ans = [], []
        def dfs(i):
            if i == len(s): 
                ret.append(ans[:])
                return
            
            for j in range(i, len(s)):
                if isPara(i, j):
                    ans.append(s[i:j+1])
                    dfs(j+1)
                    ans.pop()

        dfs(0)

        return ret
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值