leetcode 2002. 两个回文子序列长度的最大乘积 - 比特位 - 状态压缩

给你一个字符串 s ,请你找到 s 中两个 不相交回文子序列 ,使得它们长度的 乘积最大 。两个子序列在原字符串中如果没有任何相同下标的字符,则它们是 不相交 的。

请你返回两个回文子序列长度可以达到的 最大乘积 。

子序列 指的是从原字符串中删除若干个字符(可以一个也不删除)后,剩余字符不改变顺序而得到的结果。如果一个字符串从前往后读和从后往前读一模一样,那么这个字符串是一个 回文字符串 。

示例 1:

在这里插入图片描述

输入:s = “leetcodecom”
输出:9
解释:最优方案是选择 “ete” 作为第一个子序列,“cdc” 作为第二个子序列。
它们的乘积为 3 * 3 = 9 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-product-of-the-length-of-two-palindromic-subsequences

思路1 dfs 每个位置三种选择 - 超时

对于给定的字符串s ,每个位置有三种可能

  • 加入子串a
  • 加入字串b
  • 不加入任何子串
    当考虑完最后一个位置的时候,检查子串是不是回文串,计算乘积,取最大乘积即可

在题解中看到这个思路 C++ 可以通过,但是python实现之后超时

class Solution:
    def maxProduct(self, s: str) -> int:
        ans = [0]
        def check(ss):
            n = len(ss)
            for i in range(n//2):
                if ss[i]!=ss[n-i-1]:return False
            return True
        def dfs(s,s1,s2,index):
            if index ==len(s):
                if check(s1) and check(s2):ans[0] = max(ans[0],len(s1)*len(s2))
                return
            dfs(s,s1,s2,index+1)
            s1.append(s[index])
            dfs(s,s1,s2,index+1)
            s1.pop()
            s2.append(s[index])
            dfs(s,s1,s2,index+1)
            s2.pop()
        dfs(s, [], [], 0)
        return ans[0]

计算 s = “vfffvfvfvfvf”
需要 time 3.274299383163452 s

思路1 dfs 根据回文串首尾字符串相等的特性优化 - 超时

优化点:

  • 构成一个偶数长度的回文串需要每种字母出现偶数次
  • 当e 作为 子串的起点时,子串末尾的字母下标比最后一个e的下标还要大,就无法构成合法子串了,需要终止递归
  • 子串中存下标,可以比较回文和判断递归终止条件
class Solution:
    def maxProduct(self, s: str) -> int:
        n = len(s)
        ans = [0]

        def findQj():
            '''
            找有效区间
            '''
            c = {}
            for i in range(n):
                if s[i] not in c: c[s[i]] = []
                c[s[i]].append(i)
            h = 0
            t = n - 1
            cp = {}
            for k in c:
                nn = len(c[k])
                if nn == 1: continue
                for i in range(nn - 1):
                    if c[k][i] not in cp: cp[c[k][i]] = [set()]
                    h = min(h, c[k][i])
                    for j in range(i + 1, nn):
                        t = max(t, c[k][j])
                        cp[c[k][i]][0].add(c[k][j])
            for i in cp:
                cp[i].append(max(cp[i][0]))
            return h,t,cp
        def check(ss):
            n = len(ss)
            for i in range(n//2):
                if ss[i] not in cp or ss[n-i-1] not in cp[ss[i]][0]:return False
            return True
        def dfs(s,s1,s2,index):
            if s1 and (s1[0] in cp and s1[-1] >cp[s1[0]][1]):return
            if s2 and (s2[0] in cp and s2[-1] >cp[s2[0]][1]):return
            if index >t:
                if check(s1) and check(s2): ans[0] = max(ans[0], len(s1) * len(s2))
                return
            dfs(s,s1,s2,index+1)

            s1.append(index)
            dfs(s,s1,s2,index+1)
            s1.pop()

            s2.append(index)
            dfs(s,s1,s2,index+1)
            s2.pop()

        h,t,cp = findQj()
        dfs(s, [], [], h)
        return ans[0]

优化之后时间
计算 s = “vfffvfvfvfvf”
time 0.8058819770812988

思路2 dfs 短回文中插入字符或者回文 - 通过

import functools
from copy import deepcopy
class Solution:
    def maxProduct(self, s: str) -> int:
        n = len(s)
        if n ==2:return 1
        all_hw = [(1,{c}) for c in range(n)]
        '''
        找有效区间
        '''
        c = {}
        for i in range(n):
            if s[i] not in c:c[s[i]] = []
            c[s[i]].append(i)
        cp = []
        for k in c:
            nn = len(c[k])
            if nn == 1:continue
            for i in range(nn-1):
                for j in range(i+1,nn):
                    cp.append((c[k][i],c[k][j]))
		'''
       	回文拼接
        '''
        cp.sort()
        @functools.lru_cache(None)
        def dfs(l,r):
            tpl = []
            tpl.append((2,{l,r}))
            for v in range(l+1,r):
                tpl.append((3, {l,v,r}))
            for ll,rr in cp:
                if r<ll:break
                if l< ll and rr < r:
                    tpc = dfs(ll,rr)
                    for ln,vs in tpc:
                        tpl.append((ln+2,set([l]+list(vs)+[r])))
            return deepcopy(tpl)
        for i in range(len(cp)):
            l,r = cp[i]
            all_hw.extend(dfs(l,r))
        '''
        找乘积最大
        '''
        mtp = 0
        all_hw.sort(reverse=True)
        for i in range(len(all_hw)):
            for j in range(i+1,len(all_hw)):
                if all_hw[i][0]*all_hw[j][0] > mtp and not all_hw[i][1]&all_hw[j][1]:
                    mtp = all_hw[i][0]*all_hw[j][0]
                    break
        return mtp

在这里插入图片描述
计算 s = “vfffvfvfvfvf”
time 0.0468752384185791

思路2 + 状态压缩

将字符串的每一个位置元素映射到二进制位置中,用二进制的0 1 表示是否含有该字符,从而减少内存占用,以及列表合并的时间

import functools
from copy import deepcopy
class Solution:
    def maxProduct(self, s: str) -> int:
        n = len(s)
        if n ==2:return 1
        all_hw = [(1,1<<c) for c in range(n)] # 存1<<c
        '''
        找有效区间
        '''
        c = collections.defaultdict(list)
        for i in range(n):
            c[s[i]].append(i)
        cp = []
        for k in c:
            nn = len(c[k])
            if nn == 1:continue
            for i in range(nn-1):
                for j in range(i+1,nn):
                    cp.append((c[k][i],c[k][j]))

        cp.sort()
        @functools.lru_cache(None)
        def dfs(l,r):
            tpl = []
            tpl.append((2,(1<<l)+(1<<r))) # 存1<<l
            for v in range(l+1,r):
                tpl.append((3, (1<<l)+(1<<v)+(1<<r))) # 列表合并变成数字相加
            for ll,rr in cp:
                if r<ll:break
                if l< ll and rr < r:
                    tpc = dfs(ll,rr)
                    for ln,vs in tpc:
                        tpl.append((ln+2,(1<<l)+vs+(1<<r))) # 列表合并变成数字相加
            return deepcopy(tpl)
        for i in range(len(cp)):
            l,r = cp[i]
            all_hw.extend(dfs(l,r))
        '''
        找乘积最大
        '''
        mtp = 0
        all_hw.sort(reverse=True)
        for i in range(len(all_hw)):
            for j in range(i+1,len(all_hw)):
                if all_hw[i][0]*all_hw[j][0] > mtp and not all_hw[i][1]&all_hw[j][1]:
                    mtp = all_hw[i][0]*all_hw[j][0]
                    break
        return mtp

在这里插入图片描述
计算 s = “vfffvfvfvfvf”
time 0.042885541915893555

思路3 筛选出所有回文串- 状态压缩 - 比特位

tips:

  • 长度为n的字符串对应n个比特位
  • 所有子串即为 1 到 1<<n-1 闭区间所有情况
  • 两个子串做 与 运算为空,说明两个子串不相交
  • 按照1的个数进行倒序排序,提前终止循环
'''
比特位表示所有子串情况,筛选出回文串再找最长乘积
'''
class Solution:
    def maxProduct(self, s: str) -> int:
        n = len(s)
        if n == 2:return 1
        def ishw(zc):
            zc = bin(zc)[2:]
            i = 0
            nn = len(zc)
            j = nn-1
            cn = zc.count('1')
            while i< j:
                while zc[i] == '0' and i<j:i+=1
                while zc[j] == '0' and i<j:j-=1
                if not i<j:return cn
                if s[n-nn+i] != s[n-nn+j]:return 0
                i+=1
                j-=1
            return cn
        ans = 0
        candi = [x for x in range(1,(2<<(n-1))-1)]
        candi.sort(key=lambda x:bin(x).count('1'),reverse=True) # 按照1的个数进行排序

        for ii in range(len(candi)-1):
            i = candi[ii]
            a = ishw(i)
            if not a:continue
            for jj in range(ii+1,len(candi)):
                j = candi[jj]
                if i & j != 0:continue
                b = ishw(j)
                if b:
                    ans = max(ans,a*b)
                    break
        return ans((2<<(n-1))-2,1,-1):
            a = ishw(i)
            if not a:continue
            for j in range(i-1,0,-1):
                if i & j != 0:continue
                b = ishw(j)
                if b:
                    ans = max(ans,a*b)
        return ans

在这里插入图片描述
时间
计算 s = “vfffvfvfvfvf”
time 0.05485224723815918

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值