思路和模板
算法步骤
1、我们在字符串 S 中使用双指针中的左右指针技巧,初始化 left = right = 0,把索引左闭右开区间 [left, right) 称为一个「窗口」。
2、我们先不断地增加 right 指针扩大窗口 [left, right),直到窗口中的字符串符合要求。(得到可行解)
3、此时,我们停止增加 right,转而不断增加 left 指针缩小窗口 [left, right),直到窗口中的字符串不再符合要求。同时,每次增加 left,我们都要更新一轮结果。(优化可行解)
4、重复第 2 和第 3 步,直到 right 到达字符串 S 的尽头。
例题
76. 最小覆盖字串
套模板,滑动窗口的向右扩张与左侧收缩。
class Solution {public String minWindow(String s, String t) {// 需要的字符和窗口中的字符Map<Character, Integer> need = new HashMap<>();Map<Character, Integer> window = new HashMap<>();// 填充need字典,包含了需要的char和个数int tLength = t.length();for (int i = 0; i < tLength; ++i)need.put(t.charAt(i), need.getOrDefault(t.charAt(i), 0) + 1);// 滑动窗口 [left, right)int left = 0, right = 0;// 记录满足need条件的char个数,当valid = need.size则满足条件int valid = 0;int sLength = s.length();int start = 0, end = Integer.MAX_VALUE;while (right < sLength) {char c = s.charAt(right++);// c进入窗口if (need.containsKey(c)) {window.put(c, window.getOrDefault(c, 0) + 1);if (window.get(c).equals(need.get(c))) valid++;}// 当符合条件,则将窗口左侧收缩。跳出后已经不满足且已经得到了当前的最小范围,while (valid == need.size()) {// 收缩前更新一下结果if (right - left < end - start) {end = right;start = left;}char d = s.charAt(left++);if (need.containsKey(d)) {if (window.get(d).equals(need.get(d))) valid--;window.put(d, window.get(d) - 1);}}}return (start == 0 && end == Integer.MAX_VALUE) ? "" : s.substring(start, end);}}
567. 字符串的排列
class Solution {public boolean checkInclusion(String s1, String s2) {Map<Character, Integer> window = new HashMap<>();Map<Character, Integer> need = new HashMap<>();// [left, right)int left = 0, right = 0;int len1 = s1.length(), len2 = s2.length();int valid = 0;// Initialize needfor (int i = 0; i < len1; ++i)need.put(s1.charAt(i), need.getOrDefault(s1.charAt(i), 0) + 1);while (right < len2) {char c = s2.charAt(right++);window.put(c, window.getOrDefault(c, 0) + 1);if (need.getOrDefault(c, 0).equals(window.get(c))) valid++;// 当窗口大小达到s1的长度才可能包括s1的排列,才可以开始left收缩while (right - left >= len1) {//System.out.println(need + "\n" + window + "\n" + valid + " " + len1);// 得到结果马上返回if (valid == need.size()) return true;// 向左收缩,考虑后果char d = s2.charAt(left++);if (need.containsKey(d)) {if (need.get(d).equals(window.get(d))) valid--;window.put(d, window.get(d) - 1);}}}return false;}}
438. 找所有字母异位词
class Solution {public List<Integer> findAnagrams(String s, String p) {Map<Character, Integer> window = new HashMap<>();Map<Character, Integer> need = new HashMap<>();List<Integer> result = new ArrayList<>();int valid = 0;int left = 0, right = 0;int len1 = s.length(), len2 = p.length();for (int i = 0; i < len2; ++i)need.put(p.charAt(i), need.getOrDefault(p.charAt(i), 0) + 1);while (right < len1) {char c = s.charAt(right++);window.put(c, window.getOrDefault(c, 0) + 1);if (window.get(c).equals(need.getOrDefault(c, 0))) valid++;while (right - left >= len2) {if (valid == need.size()) result.add(left);char d = s.charAt(left++);if (need.containsKey(d)) {if (window.get(d).equals(need.get(d))) valid--;window.put(d, window.get(d) - 1);}}}return result;}}
和上面那题就基本一样。思考:既然是用map,那就无法考虑按顺序排列的问题了。
3.无重复字符的最长子串
class Solution {
public int lengthOfLongestSubstring(String s) {
Map<Character, Integer> window = new HashMap<>();
int left = 0, right = 0;
int len = s.length();
int result = 0;
while (right < len) {
char c = s.charAt(right++);
window.put(c, window.getOrDefault(c, 0) + 1);
// 算最大值,所以缩完再更新
while (window.get(c) > 1) {
char d = s.charAt(left++);
window.put(d, window.get(d) - 1);
}
result = Math.max(right - left, result);
}
return result;
}
}
