滑动窗口分为不固定窗口大小和固定窗口大小两种:(当要寻找符合某个要求的子串时常用)
1)不固定窗口大小:窗口大小会变化,当前窗口不满足要求时,整体向后移。
2) 固定窗口大小:窗口大小为一个常数,当前窗口不满足要求时,整体向后移。
滑动窗口与双指针不同的地方就在于,双指针的变化方向是双向的,可以左指针向右,也可以右指针向左,而滑动窗口是整个窗口向右移,左右指针同步。
1.3. 无重复字符的最长子串
题目描述:
给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度。
示例:
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
解答描述:
要找到不含有重复字符的最长子串的长度,采用滑动窗口的思想。
枚举每个字符作为字符串的起点,找到以该字符作为起点的没有重复的最长字符串,当有重复时,立即停止,然后滑动窗口的左指针向右移,继续枚举下一个字符串。
因为滑动窗口内的字符一定是没有重复的,所以只需要判断右窗口之外的字符即可。
(注意,在C++中由于STL有容器,可以采用unordered_set来进行查重,当set.count==0的时候表示该元素没有重复,set.count==1的时候,表示容器内已经有该元素,会造成重复)
代码:
C++滑动窗口:
class Solution {
public:
int max(int a,int b)
{
return a>b?a:b;
}
int lengthOfLongestSubstring(string s) {
int l=0;//用来标志枚举字符串的起始位置
int r=0;//用来找到当前枚举字符串的终止位置
unordered_set<char> ss;//用来去重
int n=s.size();
int max_str_len=0;
int temp_len=0;//用来记录当前枚举字符串的最长子串长度
while(l<n)
{
while(r<n && ss.count(s[r])==0)
{
ss.insert(s[r]);
r++;
temp_len++;
}
max_str_len=max(max_str_len,temp_len);
ss.erase(s[l]);
temp_len--;
l++;
}
return max_str_len;
}
};
C kmp算法:
//kmp算法
int max(int a,int b)
{
return a>b?a:b;
}
int lengthOfLongestSubstring(char * s){
int size=strlen(s);
int max_len=0;
int *next=malloc(sizeof(int)*256);
//int next[256]={0};//用来记录每一轮各数出现的次数
if(size<2)//当为空字符串时,直接返回0,当字符串只有一个字符时,返回1
{
return size;
}
memset(next,0,sizeof(next));
int j=0;
for(int i=0;i<size;i++)
{
j=max(next[s[i]],j);
max_len=max(max_len,i-j+1);
next[s[i]]=i+1;
}
return max_len;
};
2.567. 字符串的排列
题目描述:
给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。如果是,返回 true ;否则,返回 false 。
换句话说,s1 的排列之一是 s2 的 子串 。
示例:
示例 1:
输入:s1 = "ab" s2 = "eidbaooo"
输出:true
解释:s2 包含 s1 的排列之一 ("ba").
示例 2:
输入:s1= "ab" s2 = "eidboaoo"
输出:false
解答描述:
这道题的关键在于理解s2包含s1的排列,这意味着s2只要有一个子串和s1的元素种类和数量都相同即可。所以考虑用一个固定大小为s1.size()的滑动窗口来解决。
先用数组cnt1,cnt2分别记录s1的各个元素出现次数和s2当前窗口内的元素出现次数,如果cnt1和cnt2完全相同,说明匹配,返回true,否则的话,将滑动窗口整体向后移一步,更新cnt2,继续判断。
代码:
class Solution {
public:
bool checkInclusion(string s1, string s2) {
int n1=s1.size();
int n2=s2.size();
if(n1>n2)
{
return false;
}
vector<int> cnt1(26);//用来统计固定大小为n1的滑动窗口内s1各字符出现次数
vector<int> cnt2(26);//用来统计固定大小为n1的滑动窗口内s2各字符出现次数
for(int i=0;i<n1;i++)
{
cnt1[s1[i]-'a']++;
cnt2[s2[i]-'a']++;
}
if(cnt1==cnt2)
{
return true;
}
int l=0;
int r=n1;
while(r<n2)
{
cnt2[s2[l]-'a']--;
cnt2[s2[r]-'a']++;
if(cnt1==cnt2)
{
return true;
}
l++;
r++;
}
return false;
}
};