【算法刷题】leetcode hot 100 滑动窗口
文章目录
- 3. 无重复字符的最长子串
- 438. 找到字符串中所有字母异位词
- 总结
3. 无重复字符的最长子串
-
leetcode:https://leetcode.cn/problems/longest-substring-without-repeating-characters/?envType=study-plan-v2&envId=top-100-liked
-
滑动窗口
(1)右指针向右扩大窗口,直到不满足条件。
(2)左指针向右压缩窗口,直到满足条件。
(3)继续(1)-(2)操作,统计最大的窗口。
public int lengthOfLongestSubstring(String s) {
if (s == null || s.isEmpty()) {
return 0;
}
int max = 0;
int[] index = new int[128];
int i = 0, j = 0;
while (i <= j && j < s.length()) {
if (index[s.charAt(j)] == 0) {
index[s.charAt(j)]++;
j++;
}else {
max = Math.max(max, j - i );
index[s.charAt(i)]--;
i++;
}
}
max = Math.max(max, j - i );
return max;
}
438. 找到字符串中所有字母异位词
-
leetcode:https://leetcode.cn/problems/find-all-anagrams-in-a-string/description/?envType=study-plan-v2&envId=top-100-liked
-
开始的想法:先给字符串
p
排个序,然后遍历s
,取当前下标开始的长度和p相等的字符串,排序,和排过序的p
进行比较,相等就符合条件。显然这种解法可行,但效率太低。
public List<Integer> findAnagrams(String s, String p) {
List<Integer> ans = new ArrayList<>();
char[] ss = s.toCharArray();
char[] ps = p.toCharArray();
Arrays.sort(ps);
for (int i = 0; i <= ss.length - ps.length; i++){
char[] temp = s.substring(i, i + ps.length).toCharArray();
Arrays.sort(temp);
if (Arrays.equals(ps, temp)){
ans.add(i);
}
}
return ans;
}
- 滑动窗口:
初始化计数器:
- 使用数组
pCount
记录字符串p
中每个字符的频率。 - 使用数组
sCount
记录当前窗口中字符的频率。
滑动窗口:
- 遍历字符串
s
,逐步更新窗口。 - 当窗口长度超过
p
的长度时,移除窗口左端的字符。
比较频率数组:
- 每次更新窗口后,比较
sCount
和pCount
是否相等。 - 如果相等,则说明窗口中的子字符串是异位词,记录起始索引。
public List<Integer> findAnagrams2(String s, String p) {
List<Integer> ans = new ArrayList<>();
int[] c1 = new int[26];
int[] c2 = new int[26];
for (char c : p.toCharArray()) {
c1[c - 'a']++;
}
for (int i = 0; i < s.length(); i++) {
c2[s.charAt(i) - 'a']++;
// 移除窗口外的元素
if (i >= p.length()) {
c2[s.charAt(i-p.length()) - 'a']--;
}
if (Arrays.equals(c1, c2)) {
ans.add(i-p.length()+1);
}
}
return ans;
}
总结
滑动窗口是一种常见的算法技巧,主要用于解决字符串、数组等线性数据结构中的子区间问题。通过维护一个动态更新的窗口(子区间),可以高效地找到问题的解。滑动窗口适合解决的问题通常具有连续性或者需要频繁更新子区间的性质。
滑动窗口的基本思想
- 定义一个窗口:
- 通常用两个指针(
left
和right
)来定义窗口的左右边界。 - 窗口可以扩展(右边界向右移动)或收缩(左边界向右移动)。
- 通常用两个指针(
- 移动窗口:
- 根据问题要求,逐步移动右边界来扩展窗口。
- 根据条件判断是否需要收缩左边界。
- 更新答案:
- 在窗口满足某些条件时,更新答案(如最大值、最小值、计数等)。
- 窗口内数据结构:
- 为了高效维护窗口状态,通常需要使用额外的数据结构(如数组、
HashMap
或HashSet
)来记录窗口内的数据。
- 为了高效维护窗口状态,通常需要使用额外的数据结构(如数组、
int left = 0; // 窗口左边界
int right = 0; // 窗口右边界
while (right < s.length()) { // 遍历字符串/数组
// 扩展窗口(添加右边的元素)
char c = s.charAt(right);
right++;
// 根据问题更新窗口内状态(如计数、频率等)
while (窗口不满足条件) {
// 收缩窗口(移除左边的元素)
char d = s.charAt(left);
left++;
// 更新窗口内状态
}
// 如果窗口满足条件,更新答案
}