📅  最后修改于: 2023-12-03 15:12:36.599000             🧑  作者: Mango
给定一个大小为 $n$ 的数组 $A$,请你计算一组字典序最小的非空子串 $S$,使得 $S$ 中每个元素的出现次数都不小于 $k$。
输入包含多组测试数据。对于每个测试数据,第一行包含两个整数 $n$ 和 $k$,表示数组 $A$ 的大小和出现次数下限。接下来一行包含 $n$ 个整数,表示数组 $A$ 的元素。
对于每组测试数据,输出一个整数 $ans$,表示字典序最小的非空子串 $S$ 的长度。
如果不存在符合条件的子串 $S$,则输出 $-1$。
8 2
1 2 3 1 2 2 3 1
4
这里存在字典序最小的非空子串为 $[2, 3, 1, 2]$,其长度为 $4$,每个元素都出现了至少 $2$ 次。
6 3
1 2 3 1 2 3
-1
这里不存在符合条件的子串。
首先考虑暴力的双指针枚举子串,复杂度为 $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)