lucifer

LeetCode 有一些题目会给你设置陷阱,给你一些干扰信息。这个时候你需要小心,不要被他们带跑偏了。那么是什么样的陷阱呢?让我们来看一下!

题目地址(1297. 子串的最大出现次数)

https://leetcode-cn.com/problems/maximum-number-of-occurrences-of-a-substring

题目描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
给你一个字符串 s ,请你返回满足以下条件且出现次数最大的 任意 子串的出现次数:

子串中不同字母的数目必须小于等于 maxLetters 。
子串的长度必须大于等于 minSize 且小于等于 maxSize 。


示例 1:

输入:s = "aababcaab", maxLetters = 2, minSize = 3, maxSize = 4
输出:2
解释:子串 "aab" 在原字符串中出现了 2 次。
它满足所有的要求:2 个不同的字母,长度为 3 (在 minSize 和 maxSize 范围内)。
示例 2:

输入:s = "aaaa", maxLetters = 1, minSize = 3, maxSize = 3
输出:2
解释:子串 "aaa" 在原字符串中出现了 2 次,且它们有重叠部分。
示例 3:

输入:s = "aabcabcab", maxLetters = 2, minSize = 2, maxSize = 3
输出:3
示例 4:

输入:s = "abcde", maxLetters = 2, minSize = 3, maxSize = 3
输出:0


提示:

1 <= s.length <= 10^5
1 <= maxLetters <= 26
1 <= minSize <= maxSize <= min(26, s.length)
s 只包含小写英文字母。

暴力法

题目给的数据量不是很大,为 1 <= maxLetters <= 26,我们试一下暴力法。

思路

暴力法如下:

  • 先找出所有满足长度大于等于 minSize 且小于等于 maxSize 的所有子串。(平方的复杂度)
  • 对于 maxLetter 满足题意的子串,我们统计其出现次数。时间复杂度为 O(k),其中 k 为子串长度
  • 返回最大的出现次数

代码

Pythpn Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution:
def maxFreq(self, s: str, maxLetters: int, minSize: int, maxSize: int) -> int:
n = len(s)
letters = set()
cnts = dict()
res = 0
for i in range(n - minSize + 1):
length = minSize
while i + length <= n and length <= maxSize:
t = s[i:i + length]
for c in t:
if len(letters) > maxLetters:
break
letters.add(c)
if len(letters) <= maxLetters:
cnts[t] = cnts.get(t, 0) + 1
res = max(res, cnts[t])
letters.clear()
length += 1
return res

上述代码会超时。我们来利用剪枝来优化。

剪枝

思路

还是暴力法的思路,不过我们在此基础上进行一些优化。首先我们需要仔细阅读题目,如果你足够细心或者足够有经验,可能会发现其实题目中 maxSize 没有任何用处,属于干扰信息。

也就是说我们没有必要统计长度大于等于 minSize 且小于等于 maxSize 的所有子串,而是统计长度为 minSize 的所有字串即可。原因是,如果一个大于 minSize 长度的字串若是满足条件,那么该子串其中必定有至少一个长度为 minSize 的字串满足条件。因此一个大于 minSize 长度的字串出现了 n 次,那么该子串其中必定有一个长度为 minSize 的子串出现了 n 次。

代码

代码支持 Python3,Java:

Python Code:

1
2
3
4
5
6
7
8
9
10
 def maxFreq(self, s: str, maxLetters: int, minSize: int, maxSize: int) -> int:
counter, res = {}, 0
for i in range(0, len(s) - minSize + 1):
sub = s[i : i + minSize]
if len(set(sub)) <= maxLetters:
counter[sub] = counter.get(sub, 0) + 1
res = max(res, counter[sub])
return res;

# @lc code=end

Java Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 public int maxFreq(String s, int maxLetters, int minSize, int maxSize) {
Map<String, Integer> counter = new HashMap<>();
int res = 0;
for (int i = 0; i < s.length() - minSize + 1; i++) {
String substr = s.substring(i, i + minSize);
if (checkNum(substr, maxLetters)) {
int newVal = counter.getOrDefault(substr, 0) + 1;
counter.put(substr, newVal);
res = Math.max(res, newVal);
}
}
return res;
}
public boolean checkNum(String substr, int maxLetters) {
Set<Character> set = new HashSet<>();
for (int i = 0; i < substr.length(); i++)
set.add(substr.charAt(i));
return set.size() <= maxLetters;
}

关键点解析

  • 滑动窗口
  • 识别题目干扰信息
  • 看题目限制条件,对于本题有用的信息是1 <= maxLetters <= 26

扩展

我们也可以使用滑动窗口来解决,感兴趣的可以试试看。


 评论


博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议

本站使用 Material X 作为主题 。
载入天数...载入时分秒...