周赛 321 题解

plus2047 于 2022-11-27 发布

本周周赛无论按照什么标准来看都过于简单了,甚至感觉把一些陈年老题都翻出来了。

1.

image

按照等差数列求和公式算一下就行了。代码中的公式做了化简。

class Solution:
    def pivotInteger(self, n: int) -> int:
        for i in range(n + 1):
            if i * (i + 1) - i == n * (n + 1) // 2:
                return i
        return -1

2.

image

这个题目明显就有点过分简单了,其实就是求 s 子序列能够匹配的最长 t 前缀,由于过于简单,一时不敢相信。

class Solution:
    def appendCharacters(self, s: str, t: str) -> int:
        n = len(t)
        it = 0
        for c in s:
            if it < n and c == t[it]:
                it += 1
        return n - it

3.

image

这个题目也很简单,你可以先把链表过一遍,得到后缀最大,然后直接就能判断某个节点是否需要删除。

或者更灵巧一点,使用常用的单调栈技术,维护一个单调下降栈就可以了。注意维护链表指针。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNodes(self, head: Optional[ListNode]) -> Optional[ListNode]:
        stack = []
        while head:
            while stack and stack[-1].val < head.val:
                stack.pop()
            if stack:
                stack[-1].next = head
            stack.append(head)
            head = head.next
        return stack[0]

4.

image

难度一般。我们可以使用前缀相关的技巧轻易的计算出任何子区间中大于 k 的数字个数,或者小于 k 的数字个数,然后快速判断该区间是否满足要求。

满足要求的区间必须包含 k. 如果区间在 k 左边或者右边(单侧),这样的区间个数最多只有 N 个,枚举即可。

唯一的问题是覆盖 k, 两个端点分别在 k 之左和之右的情况。我们先考虑选定一个右端点,右方这些元素中,大于 k 的数字个数和小于 k 的数字个数分别记为 upper, lower. 对于合法区间,upper - lower 需要为 0 或者 1,分别对应奇数长和偶数长的区间。右方 upper - lower 可以计算,则左方 upper - lower 目标值就确定了。

于是我们可以先用类似于前缀和的技巧,把 k 左方的 diff = upper - lower 的每种取值计数预计算。这样就有一个 O(n) 解法了。代码中有详细注释。

这个问题分类讨论有点复杂,注意不要遗漏。

class Solution:
    def countSubarrays(self, nums: List[int], k: int) -> int:
        
        # diff_cnt 是以 k 左方某个元素开始,以 k 结束的区间中
        # diff = upper - lower 每种可能取值的计数
        diff_cnt = defaultdict(lambda: 0)

        ki = nums.index(k)
        
        upper = lower = 0
        for left in range(ki - 1, -1, -1):
            upper += nums[left] > k
            lower += nums[left] < k
            # 统计每种取值的计数
            diff_cnt[upper - lower] += 1
        
        # diff_cnt[0] 是以 k 结尾的奇数长区间中合法的个数
        # diff_cnt[1] 是以 k 结尾的偶数长区间中合法的个数
        res = diff_cnt[0] + diff_cnt[1]
        upper = lower = 0
        for right in range(ki + 1, len(nums)):
            upper += nums[right] > k
            lower += nums[right] < k
            diff = upper - lower
            # diff + x in {0, 1}
            # so x in {-diff, 1 - diff}
            res += diff_cnt[-diff] + diff_cnt[1 - diff]
            if diff == 0 or diff == 1:
                # 如果 diff = 0 or 1 则
                # 以 k 开始以当前元素结尾也是合法区间
                res += 1

        # 最后 +1 是只包含 k 的单元素区间
        return res + 1