给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度。
注意细节:
① 因为k-v k:字符 v:字符出现的索引位置。所以收缩窗口left是根据重复的字符的位置来,并不是直接++
②left根据字符索引出现的位置上+1,并且不能后退。故left=Math.max(left,map.get(c)+1)【例子:abba 】
③ max=Math.max(max,right-left);//不能写在if条件里,因为当s.length==1的时候,就只有一个字符,那么这时候如果写在if里,就没有计算窗口的长度了
class Solution {
public int lengthOfLongestSubstring(String s) {
if(s.length()==0||s==null) return 0;
Map<Character,Integer>map=new HashMap<>();//k-v k:字符 v:字符出现的索引位置
int left=0,right=0;
int max=Integer.MIN_VALUE;
while(right<s.length()){
char c=s.charAt(right);
if(map.containsKey(c)){
//abba left根据字符索引出现的位置上+1,并且不能后退
left=Math.max(left,map.get(c)+1);//收缩窗口是根据重复的字符的位置来,并不是直接++
}
map.put(c,right);
right++;
max=Math.max(max,right-left);//不能写在if条件里,因为当s.length==1的时候,就只有一个字符,那么这时候如果写在if里,就没有计算窗口的长度了
}
return max;
}
}
76、最小覆盖子串(此题是在567的固定窗口下进阶为对窗口大小不进行限制)
给你一个字符串 s
、一个字符串 t
。返回 s
中涵盖 t
所有字符的最小子串。如果 s
中不存在涵盖 t
所有字符的子串,则返回空字符串 ""
。
注意:
- 对于
t
中重复字符,我们寻找的子字符串中该字符数量必须不少于t
中该字符数量。 - 如果
s
中存在这样的子串,我们保证它是唯一的答案。
缩小窗口时,先判断后键值缩小,扩大窗口是,先键值增大后判断
//扩大窗口的
if (map.containsKey(c)) {
// window.put(c,window.getOrDefault(c,0)+1);其实并不需要将s中所有的都加入window,只需要将map中的加入即可
window.put(c, window.getOrDefault(c, 0) + 1);
if (map.get(c).equals(window.get(c))) {
sum++;
}
}
//缩小窗口的
if (map.containsKey(temp)) {
if (map.get(temp).equals(window.get(temp))) {
sum--;
}
window.put(temp, window.getOrDefault(temp, 0) - 1);
}
class Solution {
public String minWindow(String s, String t) {
Map<Character, Integer> map = new HashMap<>();
Map<Character, Integer> window = new HashMap<>();
for (char c : t.toCharArray()) {
map.put(c, map.getOrDefault(c, 0) + 1);
}
int left = 0, right = 0;
int sum = 0;
int start = 0;//start表示符合最优解的substring的起始位序
int len = Integer.MAX_VALUE;//len用来记录最终窗口的长度,并且以len作比较,淘汰选出最小的substring的len
while (right < s.length()) {
char c = s.charAt(right);
right++;
if (map.containsKey(c)) {
// window.put(c,window.getOrDefault(c,0)+1);其实并不需要将s中所有的都加入window,只需要将map中的加入即可
window.put(c, window.getOrDefault(c, 0) + 1);
if (map.get(c).equals(window.get(c))) {
sum++;
}
}
//满足条件的窗口 这里不是固定窗口,对窗口大小不限制,当map和window中的数据一致,满足条件,开始收缩
while (sum == map.size()) {
if (right - left < len) {
start = left;
len = right - left;
}
char temp = s.charAt(left);
left++;
if (map.containsKey(temp)) {
if (map.get(temp).equals(window.get(temp))) {
sum--;
}
//window.put(d,window.get(temp)-1);——bug:若一进去就将window对应的键值缩小,就永远不会满足下面的if,
// while也会一直执行,那么left越界,
// 因此,尽管和上面对窗口的处理几乎一样,但是这个处理的顺序还是很关键的!要细心!
window.put(temp, window.getOrDefault(temp, 0) - 1);
//故:缩小窗口时,先判断后键值缩小,扩大窗口是,先键值增大后判断
}
}
}
return len == Integer.MAX_VALUE ? "" : s.substring(start, start + len);
}
}
567、字符串的排列
给你两个字符串 s1
和 s2
,写一个函数来判断 s2
是否包含 s1
的排列。如果是,返回 true
;否则,返回 false
。换句话说,s1
的排列之一是 s2
的 子串 。
76、438、567三题是一样的模板,可以有同样的思路
class Solution {
public boolean checkInclusion(String s1, String s2) {
Map<Character, Integer> map = new HashMap<>();
Map<Character, Integer> window = new HashMap<>();
for (char c : s1.toCharArray()) {
map.put(c, map.getOrDefault(c, 0) + 1);
}
int left = 0, right = 0;
int sum = 0;
while (right < s2.length()) {
char c = s2.charAt(right);
right++;
if (map.containsKey(c)) {
window.put(c, window.getOrDefault(c, 0) + 1);
if (map.get(c).equals(window.get(c))) {
sum++;
}
}
while (right - left >= s1.length()) {
if (sum == map.size()) {
return true;
}
char ch = s2.charAt(left);
left++;
//right与left只隔了s1.length的距离,
//所以当boa时,right在a这里,left在b这里,这时候可以执行sum--。所以boa最后会返回false
if (map.containsKey(ch)) {
if (map.get(ch).equals(window.get(ch))) {
sum--;
}
window.put(ch, window.getOrDefault(ch, 0) - 1);
}
}
}
return false;
}
}