文章目录
给你一个字符串 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