这道题看了一下,截至我写的时候,只有103个大佬给出了这个题目的解答,而且这次的竞赛第一名的大佬也花了将近30min,基本时间都卡在这道题上面,因此能把这道题搞出来还是有些开心的。
整体的话,这道题个人感觉复杂在分类讨论上,有一些特殊情况需要注意一下,注意到了的话其实整体的思路还是挺清晰的。下面,我们就来具体看看这道题目的解答。
显然对于 N N N个独立的query要求解的话,那么我们就要求能够在至多 O ( l o g N ) O(logN) O(logN)的时间复杂度内直接求出任意一个query能否对其构成回文。
而我们考察任意一个query: ( a , b , c , d ) (a,b,c,d) (a,b,c,d),其中, ( c , d ) (c,d) (c,d)在中轴左侧对应得到的节点我们记作 ( c ′ , d ′ ) (c', d') (c′,d′)。
对于与上述query的空间不相交的部分,我们需要快速的判断这些空间内的元素是否恰好符合回文的特性,即 s i = s n − 1 − i s_i = s_{n-1-i} si=sn−1−i,这个,我们可以通过segment tree来在 O ( l o g N ) O(logN) O(logN)的时间复杂度内对任意区间内判断其是否都符合回文特性。
而对于相交的部分,本质上,我们只需要判断这些部分的字符种类和数目是否完全一致,我们就能够判断其是否能够构成回文,但是,他们的交叉性关系会使得我们的讨论略微复杂。
这里,出于讨论的简单性,我们不妨假设 a ≤ c ′ a \leq c' a≤c′,此时,我们即会有以下几种交叉情况:
下面,我们来对着三种情况进行分类讨论:
独立关系
此时,我们只需判断两个区间是否都满足字符一致性即可。
包含关系
此时,我们只需要判断大的区间的字符一致性即可。
交叉关系
交叉关系是其中最为复杂的一种情况,我们需要判断交叉的大区间内的字符一致性,即 ( a , d ′ ) (a, d') (a,d′)与 ( c , b ′ ) (c, b') (c,b′)两端区间里面的字符一致性。
除此之外,显然 ( b , d ′ ) (b, d') (b,d′)区间的字符不能交换,但是必须由 ( c , d ) (c,d) (c,d)之间的字符交换顺序得到,同样的, ( d , b ′ ) (d, b') (d,b′)区间的字符也不能变换顺序,只能由 ( a , b ) (a,b) (a,b)区间的字符变换顺序得到,因此我们需要额外多加两个包含性关系判断条件。
综上,组合三种关系下的判断条件,我们就能够获得整个题目的解答。
给出python代码实现如下:
class SegmentTree:
def __init__(self, arr):
self.length = len(arr)
self.tree = self.build(arr)
def feature_func(self, *args):
return sum(args)
def build(self, arr):
n = len(arr)
tree = [0 for _ in range(2*n)]
for i in range(n):
tree[i+n] = arr[i]
for i in range(n-1, 0, -1):
tree[i] = self.feature_func(tree[i<<1], tree[(i<<1) | 1])
return tree
def update(self, idx, val):
idx = idx + self.length
self.tree[idx] = val
while idx > 1:
self.tree[idx>>1] = self.feature_func(self.tree[idx], self.tree[idx ^ 1])
idx = idx>>1
return
def query(self, lb, rb):
lb += self.length
rb += self.length
nodes = []
while lb < rb:
if lb & 1 == 1:
nodes.append(self.tree[lb])
lb += 1
if rb & 1 == 0:
nodes.append(self.tree[rb])
rb -= 1
lb = lb >> 1
rb = rb >> 1
if lb == rb:
nodes.append(self.tree[rb])
return self.feature_func(*nodes)
class Solution:
def canMakePalindromeQueries(self, s: str, queries: List[List[int]]) -> List[bool]:
n, m = len(s), len(s) // 2
cnt = [[0 for _ in range(26)] for _ in range(n+1)]
for i, ch in enumerate(s):
for j in range(26):
cnt[i+1][j] = cnt[i][j]
cnt[i+1][ord(ch) - ord('a')] += 1
matched = [1 if s[i] == s[n-1-i] else 0 for i in range(m)]
segment_tree = SegmentTree(matched)
@lru_cache(None)
def query_inner(a, b, c, d):
lb = min(a, n-1-d)
rb = max(b, n-1-c)
cnt1 = [cnt[rb+1][i]-cnt[lb][i] for i in range(26)]
cnt2 = [cnt[n-lb][i]-cnt[n-1-rb][i] for i in range(26)]
ans = all(x == y for x, y in zip(cnt1, cnt2))
return ans
@lru_cache(None)
def query_inclusive(a, b, c, d):
cnt1 = [cnt[b+1][i]-cnt[a][i] for i in range(26)]
cnt2 = [cnt[d+1][i]-cnt[c][i] for i in range(26)]
ans = all([x >= y for x, y in zip(cnt1, cnt2)])
return ans
@lru_cache(None)
def query_outer(a, b):
if b < a:
ans = True
else:
ans = (b-a+1 == segment_tree.query(a, b))
return ans
@lru_cache(None)
def query(a, b, c, d):
a1, b1, c1, d1 = n-1-b, n-1-a, n-1-d, n-1-c
if b < c1:
return query_inner(a, b, n-1-a, n-1-b) and query_inner(c1, d1, c, d) and query_outer(0, a-1) and query_outer(b+1, c1-1) and query_outer(d1+1, m-1)
elif a > d1:
return query_inner(a, b, n-1-a, n-1-b) and query_inner(c1, d1, c, d) and query_outer(0, c1-1) and query_outer(d1+1, a-1) and query_outer(b+1, m-1)
elif a <= c1 <= d1 <= b or c1 <= a <= b <= d1:
return query_inner(a, b, c, d) and query_outer(0, min(a, c1)-1) and query_outer(max(b, d1)+1, m-1)
elif a <= c1 <= b <= d1:
return query_inner(a, b, c, d) and query_outer(0, a-1) and query_outer(d1+1, m-1) and query_inclusive(a, b, d+1, b1) and query_inclusive(c, d, b+1, d1)
else:
return query_inner(a, b, c, d) and query_outer(0, c1-1) and query_outer(b+1, m-1) and query_inclusive(a, b, a1, c-1) and query_inclusive(c, d, c1, a-1)
ans = [query(a, b, c, d) for a, b, c, d in queries]
return ans
提交代码评测得到:耗时3414ms,占用内存76.1MB。