📅  最后修改于: 2023-12-03 14:55:27.867000             🧑  作者: Mango
有一个长度为 $n$ 的字符串 $s$,和一个整数 $k$。定义一个子串的权重为其每个字符的出现次数的和。请你找到满足权重和至多为 $k$ 的子串的个数。
例如,对于字符串 $s = "aabbcc"$ 和 $k=4$,存在 $5$ 个子串权重和不超过 $4$。它们分别为:"a", "b", "c", "aa", "bb"。
我们可以使用一个滑动窗口的方法来解决该问题。具体的,设当前滑动窗口的左端点为 $l$,右端点为 $r$,那么我们按如下的方式移动右端点:
在停止移动右端点之后,我们就将以 $r$ 结尾的满足权重和至多为 $k$ 的子串计入答案。特别地,对于长度为 $l$ 至 $r$ 的子串权重和不超过 $k$,根据基本不等式我们知道其权重和必然不超过 $l \times \max_{i\in [l,r]} cnt_i$,其中 $cnt_i$ 表示 $s_i$ 在该子串中出现的次数。因此我们只需要记录 $cnt_i$ 即可在线性时间内计算当前子串的权重和。
下面给出基于滑动窗口的代码实现。
def count_substrings(s: str, k: int) -> int:
cnt = [0] * 26 # 记录当前子串中每个字符出现次数
l, r = 0, -1 # 初始化滑动窗口的左右端点
ans = 0 # 记录答案
while r < len(s) - 1:
r += 1 # 右端点右移
cnt[ord(s[r]) - ord('a')] += 1 # 累加新字符的出现次数
while l <= r and sum(cnt) > k:
cnt[ord(s[l]) - ord('a')] -= 1 # 将左端点对应的字符出现次数减 1
l += 1 # 左端点右移
ans += (r - l + 1) # 计入以 r 结尾的满足条件的子串个数
return ans
该算法的时间复杂度为 $O(n)$,其中 $n$ 是字符串 $s$ 的长度。证明如下:
该算法的空间复杂度为 $O(1)$。