滑动窗口本质上是一种非常好理解的算法,用一道例题来展示:
如何在一个字符串中找到包含另一个字符串的最短子字符串?这个题目看着有点绕,是leetcode一道Hard题。
下面来写一下滑动窗口框架:
public string MinWindow(string s,string t)
{
Dictionary<char, int> need = new Dictionary<char, int>();
Dictionary<char, int> window = new Dictionary<char, int>();
int left = 0;
int right = 0;
int startIndex = 0;
int len = 0;
//valid表示包含t中某个字符且数量相同的个数
//如果valid==need.Count说明这就是一个符合题意的子串
int valid = 0;
//将t中所有字符填充到need字典中
foreach (var item in t)
{
if (!need.ContainsKey(item))
{
need.Add(item, 1);
}
else
{
need[item]++;
}
}
while (right < s.Length)
{
char c = s[right];
right++;
//我们只关心need中的字符
if (need.ContainsKey(c))
{
//如果是,则表示window中可以添加这个字符
if (!window.ContainsKey(c))
{
window.Add(c, 1);
}
else
{
window[c]++;
}
//添加完后,检测是否满足该字符对应的数量,这也就是包含子串的意义
if (window[c] == need[c])
{
valid++;
}
}
//一般在这里做debug
//说明这是一个符合题意的子串
while (valid == need.Count)
{
//保存当前情况的
if (right - left < len)
{
len = right - left;
startIndex = left;
}
char d = s[left];
left++;
//同样我们只关心该元素是不是need中的字符
if (need.ContainsKey(d))
{
//如果window中该字符数量和need相同,说明valid需要减一了
if (window[d] == need[d])
{
valid--;
}
window[d]--;
}
}
}
return len == int.MaxValue ? "" : s.Substring(startindex, len);
}
}
注释写的很详细这里就不过多解释了,总之是两部曲:
1.当right没有到达s末端时,扩大窗口,向右移动,并更新window状态和valid状态
2.当valid满足要求时,更新结果,同时右移left缩小窗口,并更新window状态和valid状态
注意这里关键还需要对一些边界条件进行检测,按照这个模板来做问题会少许多。
还有就是注意如何存储数据,这里对包括子字符串使用了char为键的字典,就可以很好的处理问题。
labuladong公众号中有文章做了更详细的描述,欢迎大家关注。