📜  门| GATE CS 2018 |简体中文问题2(1)

📅  最后修改于: 2023-12-03 15:12:36.599000             🧑  作者: Mango

GATE CS 2018 简体中文问题2

题目描述

给定一个大小为 $n$ 的数组 $A$,请你计算一组字典序最小的非空子串 $S$,使得 $S$ 中每个元素的出现次数都不小于 $k$。

输入格式

输入包含多组测试数据。对于每个测试数据,第一行包含两个整数 $n$ 和 $k$,表示数组 $A$ 的大小和出现次数下限。接下来一行包含 $n$ 个整数,表示数组 $A$ 的元素。

输出格式

对于每组测试数据,输出一个整数 $ans$,表示字典序最小的非空子串 $S$ 的长度。

如果不存在符合条件的子串 $S$,则输出 $-1$。

样例输入1
8 2
1 2 3 1 2 2 3 1
样例输出1
4
样例解释1

这里存在字典序最小的非空子串为 $[2, 3, 1, 2]$,其长度为 $4$,每个元素都出现了至少 $2$ 次。

样例输入2
6 3
1 2 3 1 2 3
样例输出2
-1
样例解释2

这里不存在符合条件的子串。

提示
  • $1 \leqslant n \leqslant 10^6$
  • $1 \leqslant A_i \leqslant 10^6$
  • $1 \leqslant k \leqslant n$
思路

首先考虑暴力的双指针枚举子串,复杂度为 $O(n^2)$,时间复杂度无法承受。

考虑如何优化时间复杂度,首先发现可以通过分治来减小问题的规模,如果一个区间内存在符合条件的子串,则该区间可以分为三部分:

  • 左侧存在符合条件的子串;
  • 右侧存在符合条件的子串;
  • 两侧都不存在符合条件的子串。

对于第一种和第二种情况,可以递归求解;而对于第三种情况,需要对子串的每个元素计数来判断它是否符合条件,这一步可以用哈希表实现,时间复杂度为 $O(n)$,总时间复杂度为 $O(n \log n)$。

另外还需要注意如何判断一个区间内是否存在符合条件的子串,可以通过二分来判断是否存在长度 $\geqslant k$ 的子串的出现次数全部 $\geqslant k$,时间复杂度为 $O(n \log n)$。

代码
from collections import defaultdict
from typing import List, Tuple


def find_smallest_substring(n: int, k: int, a: List[int]) -> int:
    def count_chars(start: int, end: int) -> bool:
        counter = defaultdict(int)
        for i in range(start, end + 1):
            counter[a[i]] += 1
        for count in counter.values():
            if count < k:
                return False
        return True

    def search_substring(start: int, end: int) -> int:
        if end - start + 1 < k:
            return -1
        if count_chars(start, end):
            return end - start + 1
        mid = (start + end) // 2
        left_ans = search_substring(start, mid)
        right_ans = search_substring(mid + 1, end)
        if left_ans != -1 and right_ans != -1:
            return min(left_ans, right_ans)
        if left_ans != -1:
            return left_ans
        if right_ans != -1:
            return right_ans
        return -1

    return search_substring(0, n - 1)