📅  最后修改于: 2023-12-03 14:54:26.280000             🧑  作者: Mango
给定一个长度为n的字符串s和权重数组w,定义s的一个子串为s[left:right+1](其中0<=left<=right<n)。给每个子串分配权重,即s[left:right+1]的权重为w[left]+w[left+1]+…+w[right]。现在需要找到所有权重之和不超过K的子串个数。
我们可以使用暴力枚举的方法来找到所有子串,并计算它们的权重之和。时间复杂度为$O(n^3)$,会超时。
def count_substrings(s: str, w: List[int], k: int) -> int:
n = len(s)
res = 0
for left in range(n):
for right in range(left, n):
sum_w = sum(w[left:right+1])
if sum_w <= k:
res += 1
return res
我们可以使用双指针来优化暴力枚举。时间复杂度为$O(n^2)$,依然会超时。
def count_substrings(s: str, w: List[int], k: int) -> int:
n = len(s)
res = 0
for left in range(n):
sum_w = 0
right = left
while right < n:
sum_w += w[right]
if sum_w <= k:
res += 1
else:
break
right += 1
return res
我们可以先对权重数组求出前缀和,然后对于每一个右端点,找到满足条件的最小左端点。时间复杂度为$O(nlogn)$。
def count_substrings(s: str, w: List[int], k: int) -> int:
n = len(s)
pre_sum = [0] * (n + 1)
for i in range(1, n + 1):
pre_sum[i] = pre_sum[i - 1] + w[i - 1]
res = 0
for right in range(n):
left = bisect_left(pre_sum, pre_sum[right + 1] - k)
res += right - left + 1
return res
其中,bisect_left
是python标准库中的二分查找函数。
我们可以使用滑动窗口来优化前缀和+二分的方法。滑动窗口维护一个区间,使得区间内所有子串的权重之和不超过K。时间复杂度为$O(n)$。
def count_substrings(s: str, w: List[int], k: int) -> int:
n = len(s)
left = right = sum_w = res = 0
while right < n:
sum_w += w[right]
while sum_w > k:
sum_w -= w[left]
left += 1
res += right - left + 1
right += 1
return res
本文介绍了四种方法来求所有子串的权重总和不超过K的个数。暴力枚举和双指针时间复杂度为$O(n^3)$和$O(n^2)$,不适合大规模数据。前缀和+二分和滑动窗口时间复杂度为$O(nlogn)$和$O(n)$,其中滑动窗口方法最优。