这个题解利用了两步主要的算法技巧来解决问题:首先,它使用了双指针技术来计算所有可能的符合条件的子数组的数量。具体来说,外层循环遍历数组,内层循环扩展直到子数组中可以被p整除的元素数量超过k为止。在每次外层循环中,计算从当前索引起始的、满足条件的子数组数量,并逐步减少被p整除的元素的计数。其次,利用后缀数组来识别重复的子数组。构建后缀数组并计算最长公共前缀(LCP),从而可以快速确定哪些子数组是重复的。最后,将找到的总子数组数减去重复的子数组数,得到答案。
时间复杂度: O(n log n)
空间复杂度: O(n)
class SuffixArray:
def __init__(self):
return
@staticmethod
def build(s, sig):
n = len(s)
sa = list(range(n)) # 后缀数组初始化
rk = s[:] # 初始化排名数组为字符数组
ll = 0 # 已排序的长度
tmp = [0] * n # 临时数组,用于交换
while True:
# 合并排序的核心部分
p = [i for i in range(n - ll, n)] + [x - ll for i, x in enumerate(sa) if x >= ll]
cnt = [0] * sig
for i in range(n):
cnt[rk[i]] += 1
for i in range(1, sig):
cnt[i] += cnt[i - 1]
for i in range(n - 1, -1, -1):
w = rk[p[i]]
cnt[w] -= 1
sa[cnt[w]] = p[i]
sig = -1
for i in range(n):
if i == 0 or not equal(sa[i], sa[i - 1], ll):
sig += 1
tmp[sa[i]] = sig
for i in range(n):
rk[i] = tmp[i]
sig += 1
if sig == n:
break
ll = ll << 1 if ll > 0 else 1
k = 0
height = [0] * n
for i in range(n):
if rk[i] > 0:
j = sa[rk[i] - 1]
while i + k < n and j + k < n and s[i + k] == s[j + k]:
k += 1
height[rk[i]] = k
k = 0 if k - 1 < 0 else k - 1
return sa, rk, height
class Solution:
def countDistinct(self, nums: List[int], k: int, p: int) -> int:
cnt = res = 0
n = len(nums)
j = 0
right = [0] * n
for i in range(n):
while j < n and cnt + (nums[j] % p == 0) <= k:
cnt += (nums[j] % p == 0)
j += 1
res += j - i
right[i] = j
cnt -= (nums[i] % p == 0)
sa, rk, height = SuffixArray().build(nums, max(nums)+1)
dup = sum(min(height[i], right[sa[i]] - sa[i]) for i in range(1, n))
res -= dup
return res