1、题目描述
2、题目解析
参照【滑动窗口】通用解题模板,直接套用即可,
对应我之前总结的链接为:算法分享系列--滑动窗口问题
以下是【JAVA】实现的完整版本:
class Solution {
//【滑动窗口】经典类题目,s2是否包含s1的序列
public boolean checkInclusion(String s1, String s2) {
Map<Character,Integer> need = new HashMap<Character,Integer>();
Map<Character,Integer> window = new HashMap<Character,Integer>();
for(char c : s1.toCharArray()){
if(need.containsKey(c)){
need.put(c, need.get(c)+1);
}else{
need.put(c, 1);
}
}
//开始初始化窗口的相关参数
int left = 0; int right = 0;
int valid = 0;
while(right < s2.length()){
char c = s2.charAt(right);
//滑动窗口右移
right++;
if(window.containsKey(c)){
window.put(c, window.get(c)+1);
}else{
window.put(c, 1);
}
//如果该右边字符是need中包含字符,则进行相关校验
if(need.containsKey(c)){
// 【注意】这里进行比较要用 equals,不用 ==
// 因为 Integer 在大于127 的时候 不从常量池里拿,是个对象
if(need.get(c).equals(window.get(c))){
valid++;
}
}
//判断左侧窗口是否需要收紧,此时就是时刻以【right-left】的长度进行校验
while( right-left >= s1.length()){
// 在这里判断是否找到了合法的子串
if(valid == need.size()){
return true;
}
//进行左边窗口缩紧
char d = s2.charAt(left);
left++;
if(need.containsKey(d)){
if(need.get(d).equals(window.get(d))){
valid--;
}
}
window.put(d, window.get(d)-1);
}
}
// 未找到符合条件的子串
return false;
}
}
复杂度分析
时间复杂度:最坏情况下左右指针对 【s2】 的每个元素各遍历一遍,哈希表中对 【s2】 中的每个元素各插入、删除一次,对 【s1】 中的元素各插入一次。每次检查是否可行会遍历整个 【s1】 的哈希表,哈希表的大小与字符集的大小有关,则渐进时间复杂度为 O(∣s1∣+∣s2∣)。
空间复杂度:这里用了两张哈希表作为辅助空间,每张哈希表最多不会存放超过字符集大小的键值对,我们设字符集大小都不会超过字符串本身 ,则渐进空间复杂度为 O(∣s1∣+∣s2∣)。
备注:
1、代码中需要注意部分,均已经用注释备注完毕
2、 比如这里一定 要使用 equals, 而不使用 ==
// 【注意】这里进行比较要用 equals,不用 ==
// 因为 Integer 在大于127 的时候 不从常量池里拿,是个对象
if(need.get(c).equals(window.get(c))){
3、本题移动 left
缩小窗口的时机是窗口大小大于s1.length() 时,应为排列嘛,显然长度应该是一样的。对应代码如下。
//判断左侧窗口是否需要收紧,此时就是时刻以【right-left】的长度进行校验
while( right-left >= s1.length()){
4、当发现 valid == need.size() 时,就说明窗口中就是一个合法的排列,所以立即返回 true。
至于如何处理窗口的扩大和缩小,和最小覆盖子串完全相同。