Leetcode 2983. Palindrome Rearrangement Queries

  • Leetcode 2983. Palindrome Rearrangement Queries
    • 1. 解题思路
    • 2. 代码实现
  • 题目链接:2983. Palindrome Rearrangement Queries

1. 解题思路

这道题看了一下,截至我写的时候,只有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=sn1i,这个,我们可以通过segment tree来在 O ( l o g N ) O(logN) O(logN)的时间复杂度内对任意区间内判断其是否都符合回文特性。

而对于相交的部分,本质上,我们只需要判断这些部分的字符种类和数目是否完全一致,我们就能够判断其是否能够构成回文,但是,他们的交叉性关系会使得我们的讨论略微复杂。

这里,出于讨论的简单性,我们不妨假设 a ≤ c ′ a \leq c' ac,此时,我们即会有以下几种交叉情况:

  1. 独立关系: a ≤ b < c ′ ≤ d ′ a \leq b < c' \leq d' ab<cd
  2. 包含关系: a ≤ c ′ ≤ d ′ ≤ b a \leq c' \leq d' \leq b acdb
  3. 交叉关系: a ≤ c ′ ≤ b ≤ d ′ a \leq c' \leq b \leq d' acbd

下面,我们来对着三种情况进行分类讨论:

  1. 独立关系

    此时,我们只需判断两个区间是否都满足字符一致性即可。

  2. 包含关系

    此时,我们只需要判断大的区间的字符一致性即可。

  3. 交叉关系

    交叉关系是其中最为复杂的一种情况,我们需要判断交叉的大区间内的字符一致性,即 ( 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)区间的字符变换顺序得到,因此我们需要额外多加两个包含性关系判断条件。

综上,组合三种关系下的判断条件,我们就能够获得整个题目的解答。

2. 代码实现

给出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。

你可能感兴趣的:(leetcode笔记,leetcode,2983,leetcode周赛378,leetcode,hard,分类讨论,回文)