LC 730. Count Different Palindromic Subsequences
https://leetcode.com/problems/count-different-palindromic-subsequences/
其实这题可以化简成为,对于S[0..n-1]字符串来说:
- A: 字符串S[0..n-1]头尾是'a'的回文个数
- B,C,D: 字符串S[0..n-1]头尾是'b''c''d'的回文个数
- 每一类回文因为头尾字符不同肯定是不同的
- 那么结果就是A+B+C+D
那么如何求解S[0..n-1]头尾是'a'的回文个数呢?
- 假设最左边'a'是s, 最右边'a'是e的话,
- 那么就变为了S[s+1, e-1]存在多少个不相同的回文串。
所以统计去重的话,我们可以要求在统计的时候,加上某些限定的条件(比如固定某个前缀等)。一旦加上这些限定条件之后,所得到的结果肯定是不重复的。
在计算到诸如 "a…a"这样的话,是有三类选择的:
- 只使用a
- 只使用aa
- 使用fun(…, a/b/c/d)
def fun(s, e, c):
if s > e:
return 0
key = (s, e, c)
if key in dp:
return dp[key]
# print(s, e, c)
s = search_right(s, c)
e = search_left(e, c)
# print(s, e)
if s > e:
ans = 0
elif s == e:
ans = 1
else:
ans = 2 # 'xx or x'
for x in range(4):
ans += fun(s + 1, e - 1, x)
ans = ans % P
dp[key] = ans
return ans
然后另外一个关键就是如何实现 `search_left` 和 `search_right`, 就是找到某个位置之后/之前的最近的某个字符。一种办法使用二分搜索,一个办法则是创建左右索引,这点实现差别对耗时影响还蛮大的,二分搜索大约是10420ms, 左右索引是4052ms.
use_array_index = True
if use_array_index:
most_right = [n] * 4
right = [[n] * 4 for _ in range(n)]
for i in reversed(range(n)):
x = ord(S[i]) - ord('a')
most_right[x] = i
right[i] = most_right.copy()
most_left = [-1] * 4
left = [[-1] * 4 for _ in range(n)]
for i in range(n):
x = ord(S[i]) - ord('a')
most_left[x] = i
left[i] = most_left.copy()
def search_right(p, c):
return right[p][c]
def search_left(p, c):
return left[p][c]